From 62ccca1427a9fe3787cf002bff6102d62d683a0a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 6 Apr 2011 03:53:30 +0200 Subject: [PATCH 01/15] make docs now works (needs doxygen installed), added documentation to a LOT of stuff, refactored most utility functions, complete rewrites of DDV sockets and the FLV parsers, updated HTTP_Box_Parser and Connector_HTTP to be able to cope with these new changes - Connector_RTMP, Buffer and Connector_RAW are currently broken. Need to be fixed ASAP. --- Doxyfile | 296 +++++++++++++++++++++++++++++++ util/MP4/box.cpp | 1 + util/ddv_socket.cpp | 402 +++++++++++++++++++++++++++++------------- util/ddv_socket.h | 53 ++++++ util/flv.cpp | 88 --------- util/flv_data.cpp | 87 --------- util/flv_pack.cpp | 123 ------------- util/flv_sock.cpp | 106 ----------- util/flv_tag.cpp | 391 ++++++++++++++++++++++++++++++++++++++++ util/flv_tag.h | 40 +++++ util/http_parser.cpp | 66 ++----- util/http_parser.h | 41 +++++ util/server_setup.cpp | 39 ++-- 13 files changed, 1132 insertions(+), 601 deletions(-) create mode 100644 Doxyfile create mode 100644 util/ddv_socket.h delete mode 100644 util/flv.cpp delete mode 100644 util/flv_data.cpp delete mode 100644 util/flv_pack.cpp delete mode 100644 util/flv_sock.cpp create mode 100644 util/flv_tag.cpp create mode 100644 util/flv_tag.h create mode 100644 util/http_parser.h diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 00000000..dd8ba205 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,296 @@ +# Doxyfile 1.6.3 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = DDVTECH Streaming Server +PROJECT_NUMBER = 1 +OUTPUT_DIRECTORY = ./docs +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = YES +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 2 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +BUILTIN_STL_SUPPORT = YES +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +INPUT = . +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = */.git/* +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_TIMESTAMP = YES +HTML_ALIGN_MEMBERS = YES +HTML_DYNAMIC_SECTIONS = NO + +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project + +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO + +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = + +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +USE_INLINE_TREES = NO +TREEVIEW_WIDTH = 250 +FORMULA_FONTSIZE = 10 +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_FONTNAME = FreeSans +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES + diff --git a/util/MP4/box.cpp b/util/MP4/box.cpp index 57ccbb93..9f790b7f 100644 --- a/util/MP4/box.cpp +++ b/util/MP4/box.cpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index b1bd03e0..2835a5cf 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -1,141 +1,112 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "ddv_socket.h" -bool socketError = false; -bool socketBlocking = false; +/// Create a new base socket. This is a basic constructor for converting any valid socket to a DDV::Socket. +/// \param sockNo Integer representing the socket to convert. +DDV::Socket::Socket(int sockNo){ + sock = sockNo; + Error = false; + Blocking = false; +}//DDV::Socket basic constructor -int DDV_OpenUnix(std::string adres, bool nonblock = false){ - int s = socket(PF_UNIX, SOCK_STREAM, 0); +/// Close connection. The internal socket is closed and then set to -1. +void DDV::Socket::close(){ + #if DEBUG >= 3 + fprintf(stderr, "Socket closed.\n"); + #endif + ::close(sock); + sock = -1; +}//DDV::Socket::close + +/// Returns internal socket number. +int DDV::Socket::getSocket(){return sock;} + +/// Create a new Unix Socket. This socket will (try to) connect to the given address right away. +/// \param address String containing the location of the Unix socket to connect to. +/// \param nonblock Whether the socket should be nonblocking. False by default. +DDV::Socket::Socket(std::string address, bool nonblock){ + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0){ + #if DEBUG >= 1 + fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + #endif + return; + } + Error = false; + Blocking = false; sockaddr_un addr; addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, adres.c_str(), adres.size()+1); - int r = connect(s, (sockaddr*)&addr, sizeof(addr)); + strncpy(addr.sun_path, address.c_str(), address.size()+1); + int r = connect(sock, (sockaddr*)&addr, sizeof(addr)); if (r == 0){ if (nonblock){ - int flags = fcntl(s, F_GETFL, 0); + int flags = fcntl(sock, F_GETFL, 0); flags |= O_NONBLOCK; - fcntl(s, F_SETFL, flags); - } - return s; - }else{ - close(s); - return 0; - } -} - -int DDV_Listen(int port){ - int s = socket(AF_INET, SOCK_STREAM, 0); - int on = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port);//port 8888 - inet_pton(AF_INET, "0.0.0.0", &addr.sin_addr);//listen on all interfaces - int ret = bind(s, (sockaddr*)&addr, sizeof(addr));//bind to all interfaces, chosen port - if (ret == 0){ - ret = listen(s, 100);//start listening, backlog of 100 allowed - if (ret == 0){ - return s; - }else{ - fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); - close(s); - return 0; + fcntl(sock, F_SETFL, flags); } }else{ - fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); - close(s); - return 0; + #if DEBUG >= 1 + fprintf(stderr, "Could not connect to %s! Error: %s\n", address.c_str(), strerror(errno)); + #endif + close(); } -} +}//DDV::Socket Unix Contructor -int DDV_UnixListen(std::string adres, bool nonblock = false){ - unlink(adres.c_str()); - int s = socket(AF_UNIX, SOCK_STREAM, 0); - if (nonblock){ - int flags = fcntl(s, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(s, F_SETFL, flags); - } - sockaddr_un addr; - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, adres.c_str(), adres.size()+1); - int ret = bind(s, (sockaddr*)&addr, sizeof(addr)); - if (ret == 0){ - ret = listen(s, 100);//start listening, backlog of 100 allowed - if (ret == 0){ - return s; - }else{ - fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); - close(s); - return 0; - } - }else{ - fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); - close(s); - return 0; - } -} - -int DDV_Accept(int sock, bool nonblock = false){ - int r = accept(sock, 0, 0); - if ((r >= 0) && nonblock){ - int flags = fcntl(r, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(r, F_SETFL, flags); - } - return r; -} - -bool DDV_write(const void * buffer, int todo, int sock){ - int sofar = 0; - socketBlocking = false; - while (sofar != todo){ - int r = send(sock, (char*)buffer + sofar, todo-sofar, 0); - if (r <= 0){ - switch (errno){ - case EWOULDBLOCK: socketBlocking = true; break; - default: - socketError = true; - fprintf(stderr, "Could not write! %s\n", strerror(errno)); - return false; - break; - } - } - sofar += r; - } - return true; -} - -signed int DDV_ready(int sock){ +/// Returns the ready-state for this socket. +/// \returns 1 if data is waiting to be read, -1 if not connected, 0 otherwise. +signed int DDV::Socket::ready(){ + if (sock < 0) return -1; char tmp; int preflags = fcntl(sock, F_GETFL, 0); int postflags = preflags | O_NONBLOCK; fcntl(sock, F_SETFL, postflags); int r = recv(sock, &tmp, 1, MSG_PEEK); fcntl(sock, F_SETFL, preflags); + if (r < 0){ + if (errno == EAGAIN || errno == EWOULDBLOCK){ + return 0; + }else{ + #if DEBUG >= 2 + fprintf(stderr, "Socket ready error! Error: %s\n", strerror(errno)); + #endif + close(); + return -1; + } + } + if (r == 0){ + close(); return -1; + } return r; } -bool DDV_read(void * buffer, int todo, int sock){ +/// Returns the connected-state for this socket. +/// Note that this function might be slightly behind the real situation. +/// The connection status is updated after every read/write attempt, when errors occur +/// and when the socket is closed manually. +/// \returns True if socket is connected, false otherwise. +bool DDV::Socket::connected(){ + return (sock >= 0); +} + +/// Writes data to socket. This function blocks if the socket is blocking and all data cannot be written right away. +/// If the socket is nonblocking and not all data can be written, this function sets internal variable Blocking to true +/// and returns false. +/// \param buffer Location of the buffer to write from. +/// \param len Amount of bytes to write. +/// \returns True if the whole write was succesfull, false otherwise. +bool DDV::Socket::write(const void * buffer, int len){ int sofar = 0; - socketBlocking = false; - while (sofar != todo){ - int r = recv(sock, (char*)buffer + sofar, todo-sofar, 0); + Blocking = false; + while (sofar != len){ + int r = send(sock, (char*)buffer + sofar, len-sofar, 0); if (r <= 0){ switch (errno){ - case EWOULDBLOCK: socketBlocking = true; break; + case EWOULDBLOCK: Blocking = true; break; default: - socketError = true; - fprintf(stderr, "Could not read! %s\n", strerror(errno)); + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not write data! Error: %s\n", strerror(errno)); + #endif + close(); return false; break; } @@ -143,41 +114,218 @@ bool DDV_read(void * buffer, int todo, int sock){ sofar += r; } return true; -} +}//DDv::Socket::write -bool DDV_read(void * buffer, int width, int count, int sock){return DDV_read(buffer, width*count, sock);} -bool DDV_write(void * buffer, int width, int count, int sock){return DDV_write(buffer, width*count, sock);} +/// Reads data from socket. This function blocks if the socket is blocking and all data cannot be read right away. +/// If the socket is nonblocking and not all data can be read, this function sets internal variable Blocking to true +/// and returns false. +/// \param buffer Location of the buffer to read to. +/// \param len Amount of bytes to read. +/// \returns True if the whole read was succesfull, false otherwise. +bool DDV::Socket::read(void * buffer, int len){ + int sofar = 0; + Blocking = false; + while (sofar != len){ + int r = recv(sock, (char*)buffer + sofar, len-sofar, 0); + if (r <= 0){ + switch (errno){ + case EWOULDBLOCK: Blocking = true; break; + default: + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not read data! Error: %s\n", strerror(errno)); + #endif + close(); + return false; + break; + } + } + sofar += r; + } + return true; +}//DDV::Socket::read +/// Read call that is compatible with file access syntax. This function simply calls the other read function. +bool DDV::Socket::read(void * buffer, int width, int count){return read(buffer, width*count);} +/// Write call that is compatible with file access syntax. This function simply calls the other write function. +bool DDV::Socket::write(void * buffer, int width, int count){return write(buffer, width*count);} +/// Write call that is compatible with std::string. This function simply calls the other write function. +bool DDV::Socket::write(const std::string data){return write(data.c_str(), data.size());} -int DDV_iwrite(void * buffer, int todo, int sock){ - int r = send(sock, buffer, todo, 0); +/// 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. +int DDV::Socket::iwrite(void * buffer, int len){ + int r = send(sock, buffer, len, 0); if (r < 0){ switch (errno){ case EWOULDBLOCK: return 0; break; default: - socketError = true; - fprintf(stderr, "Could not write! %s\n", strerror(errno)); + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not iwrite data! Error: %s\n", strerror(errno)); + #endif + close(); return 0; break; } } return r; -} +}//DDV::Socket::iwrite -int DDV_iread(void * buffer, int todo, int sock){ - int r = recv(sock, buffer, todo, 0); +/// 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. +/// \returns The amount of bytes actually read. +int DDV::Socket::iread(void * buffer, int len){ + int r = recv(sock, buffer, len, 0); if (r < 0){ switch (errno){ - case EWOULDBLOCK: break; + case EWOULDBLOCK: return 0; break; default: - socketError = true; - fprintf(stderr, "Could not read! %s\n", strerror(errno)); + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not iread data! Error: %s\n", strerror(errno)); + #endif + close(); return 0; break; } } return r; +}//DDV::Socket::iread + +/// Create a new base ServerSocket. The socket is never connected, and a placeholder for later connections. +DDV::ServerSocket::ServerSocket(){ + sock = -1; +}//DDV::ServerSocket base Constructor + +/// Create a new TCP ServerSocket. The socket is immediately bound and set to listen. +/// A maximum of 100 connections will be accepted between accept() calls. +/// Any further connections coming in will be dropped. +/// \param port The TCP port to listen on +/// \param hostname (optional) The interface to bind to. The default is 0.0.0.0 (all interfaces). +/// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). +DDV::ServerSocket::ServerSocket(int port, std::string hostname, bool nonblock){ + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0){ + #if DEBUG >= 1 + fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + #endif + return; + } + int on = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (nonblock){ + int flags = fcntl(sock, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(sock, F_SETFL, flags); + } + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port);//set port + inet_pton(AF_INET, hostname.c_str(), &addr.sin_addr);//set interface, 0.0.0.0 (default) is all + int ret = bind(sock, (sockaddr*)&addr, sizeof(addr));//do the actual bind + if (ret == 0){ + ret = listen(sock, 100);//start listening, backlog of 100 allowed + if (ret == 0){ + return; + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } +}//DDV::ServerSocket TCP Constructor + +/// Create a new Unix ServerSocket. The socket is immediately bound and set to listen. +/// A maximum of 100 connections will be accepted between accept() calls. +/// Any further connections coming in will be dropped. +/// The address used will first be unlinked - so it succeeds if the Unix socket already existed. Watch out for this behaviour - it will delete any file located at address! +/// \param address The location of the Unix socket to bind to. +/// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). +DDV::ServerSocket::ServerSocket(std::string address, bool nonblock){ + unlink(address.c_str()); + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0){ + #if DEBUG >= 1 + fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + #endif + return; + } + if (nonblock){ + int flags = fcntl(sock, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(sock, F_SETFL, flags); + } + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, address.c_str(), address.size()+1); + int ret = bind(sock, (sockaddr*)&addr, sizeof(addr)); + if (ret == 0){ + ret = listen(sock, 100);//start listening, backlog of 100 allowed + if (ret == 0){ + return; + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } +}//DDV::ServerSocket Unix Constructor + +/// Accept any waiting connections. If the DDV::ServerSocket is blocking, this function will block until there is an incoming connection. +/// If the DDV::ServerSocket is nonblocking, it might return a DDV::Socket that is not connected, so check for this. +/// \param nonblock (optional) Whether the newly connected socket should be nonblocking. Default is false (blocking). +/// \returns A DDV::Socket, which may or may not be connected, depending on settings and circumstances. +DDV::Socket DDV::ServerSocket::accept(bool nonblock){ + if (sock < 0){return DDV::Socket(-1);} + int r = ::accept(sock, 0, 0); + if ((r >= 0) && nonblock){ + int flags = fcntl(r, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(r, F_SETFL, flags); + } + if (r < 0){ + if (errno != EWOULDBLOCK && errno != EAGAIN){close();} + } + return DDV::Socket(r); } +/// Close connection. The internal socket is closed and then set to -1. +void DDV::ServerSocket::close(){ + ::close(sock); + sock = -1; +}//DDV::ServerSocket::close +/// Returns the connected-state for this socket. +/// Note that this function might be slightly behind the real situation. +/// The connection status is updated after every accept attempt, when errors occur +/// and when the socket is closed manually. +/// \returns True if socket is connected, false otherwise. +bool DDV::ServerSocket::connected(){ + return (sock >= 0); +}//DDV::ServerSocket::connected + +/// Returns internal socket number. +int DDV::ServerSocket::getSocket(){return sock;} diff --git a/util/ddv_socket.h b/util/ddv_socket.h new file mode 100644 index 00000000..cc9421ae --- /dev/null +++ b/util/ddv_socket.h @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +///Holds DDV Socket tools. +namespace DDV{ + + /// This class is for easy communicating through sockets, either TCP or Unix. + class Socket{ + private: + int sock; ///< Internally saved socket number. + public: + Socket(int sockNo); ///< Create a new base socket. + Socket(std::string adres, bool nonblock = false); ///< Create a new Unix Socket. + bool Error; ///< Set to true if a socket error happened. + bool Blocking; ///< Set to true if a socket is currently or wants to be blocking. + signed int ready(); ///< Returns the ready-state for this socket. + bool connected(); ///< Returns the connected-state for this socket. + bool read(void * buffer, int len); ///< Reads data from socket. + bool read(void * buffer, int width, int count); ///< Read call that is compatible with file access syntax. + bool write(const void * buffer, int len); ///< Writes data to socket. + bool write(void * buffer, int width, int count); ///< Write call that is compatible with file access syntax. + bool write(const std::string data); ///< Write call that is compatible with std::string. + int iwrite(void * buffer, int len); ///< Incremental write call. + int iread(void * buffer, int len); ///< Incremental read call. + void close(); ///< Close connection. + int getSocket(); ///< Returns internal socket number. + }; + + /// This class is for easily setting up listening socket, either TCP or Unix. + class ServerSocket{ + private: + int sock; ///< Internally saved socket number. + public: + ServerSocket(); ///< Create a new base ServerSocket. + ServerSocket(int port, std::string hostname = "0.0.0.0", bool nonblock = false); ///< Create a new TCP ServerSocket. + ServerSocket(std::string adres, bool nonblock = false); ///< Create a new Unix ServerSocket. + Socket accept(bool nonblock = false); ///< Accept any waiting connections. + bool connected(); ///< Returns the connected-state for this socket. + void close(); ///< Close connection. + int getSocket(); ///< Returns internal socket number. + }; + +}; diff --git a/util/flv.cpp b/util/flv.cpp deleted file mode 100644 index 8780077d..00000000 --- a/util/flv.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include //for read() -#include -#include "flv_pack.cpp" - -char FLVHeader[13]; -bool All_Hell_Broke_Loose = false; - -//checks FLV Header for correctness -//returns true if everything is alright, false otherwise -bool FLV_Checkheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - if (header[8] != 0x09) return false; - if (header[9] != 0) return false; - if (header[10] != 0) return false; - if (header[11] != 0) return false; - if (header[12] != 0) return false; - return true; -}//FLV_Checkheader - -//returns true if header is an FLV header -bool FLV_Isheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - return true; -}//FLV_Isheader - -bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar){ - if (sofar >= count){return true;} - int r = 0; - r = fread(buffer + sofar,1,count-sofar,stdin); - if (r < 0){All_Hell_Broke_Loose = true; return false;} - sofar += r; - if (sofar >= count){return true;} - return false; -} - -//gets a packet, storing in given FLV_Pack pointer. -//will assign pointer if null -//resizes FLV_Pack data field bigger if data doesn't fit -// (does not auto-shrink for speed!) -bool FLV_GetPacket(FLV_Pack *& p){ - int preflags = fcntl(fileno(stdin), F_GETFL, 0); - int postflags = preflags | O_NONBLOCK; - fcntl(fileno(stdin), F_SETFL, postflags); - static bool done = true; - static unsigned int sofar = 0; - if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} - if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} - - if (done){ - //read a header - if (ReadUntil(p->data, 11, sofar)){ - //if its a correct FLV header, throw away and read tag header - if (FLV_Isheader(p->data)){ - if (ReadUntil(p->data, 13, sofar)){ - if (FLV_Checkheader(p->data)){ - sofar = 0; - memcpy(FLVHeader, p->data, 13); - }else{All_Hell_Broke_Loose = true;} - } - }else{ - //if a tag header, calculate length and read tag body - p->len = p->data[3] + 15; - p->len += (p->data[2] << 8); - p->len += (p->data[1] << 16); - if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} - done = false; - } - } - }else{ - //read tag body - if (ReadUntil(p->data, p->len, sofar)){ - //calculate keyframeness, next time read header again, return true - p->isKeyframe = false; - if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} - done = true; - sofar = 0; - fcntl(fileno(stdin), F_SETFL, preflags); - return true; - } - } - fcntl(fileno(stdin), F_SETFL, preflags); - return false; -}//FLV_GetPacket - diff --git a/util/flv_data.cpp b/util/flv_data.cpp deleted file mode 100644 index fde4d2c5..00000000 --- a/util/flv_data.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include //for read() -#include -#include "flv_pack.cpp" - -char FLVHeader[13]; -bool All_Hell_Broke_Loose = false; - -//checks FLV Header for correctness -//returns true if everything is alright, false otherwise -bool FLV_Checkheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - if (header[8] != 0x09) return false; - if (header[9] != 0) return false; - if (header[10] != 0) return false; - if (header[11] != 0) return false; - if (header[12] != 0) return false; - return true; -}//FLV_Checkheader - -//returns true if header is an FLV header -bool FLV_Isheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - return true; -}//FLV_Isheader - -bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P){ - if (sofar >= count){return true;} - int r = 0; - if (P+(count-sofar) > S){r = S-P;}else{r = count-sofar;} - memcpy(buffer+sofar, D+P, r); - P += r; - sofar += r; - if (sofar >= count){return true;} - return false; -} - -//gets a packet, storing in given FLV_Pack pointer. -//will assign pointer if null -//resizes FLV_Pack data field bigger if data doesn't fit -// (does not auto-shrink for speed!) -bool FLV_GetPacket(FLV_Pack *& p, char * D, unsigned int S, unsigned int & P){ - static bool done = true; - static unsigned int sofar = 0; - if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} - if (p->buf < 15){p->data = (char*)realloc(p->data, 15000000); p->buf = 15000000;} - - if (done){ - //read a header - if (ReadUntil(p->data, 11, sofar, D, S, P)){ - //if its a correct FLV header, throw away and read tag header - if (FLV_Isheader(p->data)){ - if (ReadUntil(p->data, 13, sofar, D, S, P)){ - if (FLV_Checkheader(p->data)){ - sofar = 0; - memcpy(FLVHeader, p->data, 13); - }else{All_Hell_Broke_Loose = true;} - } - }else{ - //if a tag header, calculate length and read tag body - p->len = p->data[3] + 15; - p->len += (p->data[2] << 8); - p->len += (p->data[1] << 16); - //if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} - if (p->data[0] > 0x12){ - printf("Invalid data: %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n", p->data[0], p->data[1], p->data[2], p->data[3], p->data[4], p->data[5], p->data[6]); - } - done = false; - } - } - }else{ - //read tag body - if (ReadUntil(p->data, p->len, sofar, D, S, P)){ - //calculate keyframeness, next time read header again, return true - p->isKeyframe = false; - if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} - done = true; - sofar = 0; - return true; - } - } - return false; -}//FLV_GetPacket - diff --git a/util/flv_pack.cpp b/util/flv_pack.cpp deleted file mode 100644 index a60bf6a5..00000000 --- a/util/flv_pack.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -class FLV_Pack { - public: - int len; - int buf; - bool isKeyframe; - char * data; - std::string tagType(){ - std::string R = ""; - switch (data[0]){ - case 0x09: - switch (data[11] & 0x0F){ - case 1: R += "JPEG"; break; - case 2: R += "H263"; break; - case 3: R += "ScreenVideo1"; break; - case 4: R += "VP6"; break; - case 5: R += "VP6Alpha"; break; - case 6: R += "ScreenVideo2"; break; - case 7: R += "AVC"; break; - default: R += "unknown"; break; - } - R += " video "; - switch (data[11] & 0xF0){ - case 0x10: R += "keyframe"; break; - case 0x20: R += "iframe"; break; - case 0x30: R += "disposableiframe"; break; - case 0x40: R += "generatedkeyframe"; break; - case 0x50: R += "videoinfo"; break; - } - if ((data[11] & 0x0F) == 7){ - switch (data[12]){ - case 0: R += " header"; break; - case 1: R += " NALU"; break; - case 2: R += " endofsequence"; break; - } - } - break; - case 0x08: - switch (data[11] & 0xF0){ - case 0x00: R += "linear PCM PE"; break; - case 0x10: R += "ADPCM"; break; - case 0x20: R += "MP3"; break; - case 0x30: R += "linear PCM LE"; break; - case 0x40: R += "Nelly16kHz"; break; - case 0x50: R += "Nelly8kHz"; break; - case 0x60: R += "Nelly"; break; - case 0x70: R += "G711A-law"; break; - case 0x80: R += "G711mu-law"; break; - case 0x90: R += "reserved"; break; - case 0xA0: R += "AAC"; break; - case 0xB0: R += "Speex"; break; - case 0xE0: R += "MP38kHz"; break; - case 0xF0: R += "DeviceSpecific"; break; - default: R += "unknown"; break; - } - switch (data[11] & 0x0C){ - case 0x0: R += " 5.5kHz"; break; - case 0x4: R += " 11kHz"; break; - case 0x8: R += " 22kHz"; break; - case 0xC: R += " 44kHz"; break; - } - switch (data[11] & 0x02){ - case 0: R += " 8bit"; break; - case 2: R += " 16bit"; break; - } - switch (data[11] & 0x01){ - case 0: R += " mono"; break; - case 1: R += " stereo"; break; - } - R += " audio"; - if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){ - R += " initdata"; - } - break; - case 0x12: - R += "(meta)data"; - break; - default: - R += "unknown"; - break; - } - return R; - };//tagtype - unsigned int tagTime(){ - return (data[4] << 16) + (data[5] << 8) + data[6] + (data[7] << 24); - }//tagTime getter - void tagTime(unsigned int T){ - data[4] = ((T >> 16) & 0xFF); - data[5] = ((T >> 8) & 0xFF); - data[6] = (T & 0xFF); - data[7] = ((T >> 24) & 0xFF); - }//tagTime setter - FLV_Pack(){ - len = 0; buf = 0; data = 0; isKeyframe = false; - }//empty constructor - FLV_Pack(const FLV_Pack& O){ - buf = O.len; - len = buf; - if (len > 0){ - data = (char*)malloc(len); - memcpy(data, O.data, len); - }else{ - data = 0; - } - isKeyframe = O.isKeyframe; - }//copy constructor - FLV_Pack & operator= (const FLV_Pack& O){ - if (this != &O){//no self-assignment - if (data != 0){free(data);} - buf = O.len; - len = buf; - if (len > 0){ - data = (char*)malloc(len); - memcpy(data, O.data, len); - }else{ - data = 0; - } - isKeyframe = O.isKeyframe; - } - return *this; - }//assignment operator -};//FLV_Pack diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp deleted file mode 100644 index 8b74c9aa..00000000 --- a/util/flv_sock.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "flv_pack.cpp" - -char FLVHeader[13]; -bool All_Hell_Broke_Loose = false; - -//checks FLV Header for correctness -//returns true if everything is alright, false otherwise -bool FLV_Checkheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - if (header[8] != 0x09) return false; - if (header[9] != 0) return false; - if (header[10] != 0) return false; - if (header[11] != 0) return false; - if (header[12] != 0) return false; - return true; -}//FLV_Checkheader - -//returns true if header is an FLV header -bool FLV_Isheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - return true; -}//FLV_Isheader - -bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock){ - if (sofar == count){return true;} - int r = DDV_iread(buffer + sofar,count-sofar,sock); - if (r < 0){ - if (errno != EWOULDBLOCK){ - All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); - } - return false; - } - sofar += r; - if (sofar == count){return true;} - if (sofar > count){ - All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: %s. Read too much. All Hell Broke Loose!\n", strerror(errno)); - } - return false; -} - -//gets a packet, storing in given FLV_Pack pointer. -//will assign pointer if null -//resizes FLV_Pack data field bigger if data doesn't fit -// (does not auto-shrink for speed!) -bool FLV_GetPacket(FLV_Pack *& p, int sock){ - static bool done = true; - static unsigned int sofar = 0; - if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} - if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} - - if (done){ - //read a header - if (ReadUntil(p->data, 11, sofar, sock)){ - //if its a correct FLV header, throw away and read tag header - if (FLV_Isheader(p->data)){ - if (ReadUntil(p->data, 13, sofar, sock)){ - if (FLV_Checkheader(p->data)){ - sofar = 0; - memcpy(FLVHeader, p->data, 13); - //fwrite(p->data, 13, 1, stdout);//output raw stream - }else{ - All_Hell_Broke_Loose = true; - fprintf(stderr, "Invalid FLV header. All Hell Broke Loose!\n"); - } - } - }else{ - //if a tag header, calculate length and read tag body - p->len = p->data[3] + 15; - p->len += (p->data[2] << 8); - p->len += (p->data[1] << 16); - //fprintf(stderr, "Tag of len %i\n", p->len); - if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} - done = false; - } - } - }else{ - //read tag body - if (ReadUntil(p->data, p->len, sofar, sock)){ - //calculate keyframeness, next time read header again, return true - p->isKeyframe = false; - if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} - int testlen = p->data[p->len-1] + 4; - testlen += (p->data[p->len-2] << 8); - testlen += (p->data[p->len-3] << 16); - testlen += (p->data[p->len-4] << 24); - //fwrite(p->data, p->len, 1, stdout);//output raw stream - if (p->len != testlen){ - fprintf(stderr, "Len: %i, testlen: %i\n", p->len, testlen); - All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: Wrong size tag? All Hell Broke Loose!\n"); - return false; - } - done = true; - sofar = 0; - return true; - } - } - return false; -}//FLV_GetPacket - diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp new file mode 100644 index 00000000..70321b17 --- /dev/null +++ b/util/flv_tag.cpp @@ -0,0 +1,391 @@ +#include "flv_tag.h" +#include //for Tag::FileLoader +#include //for Tag::FileLoader +#include //for Tag::FileLoader +#include //malloc +#include //memcpy +#include "ddv_socket.h" //socket functions + +char FLV::Header[13]; ///< Holds the last FLV header parsed. +bool FLV::Parse_Error = false; ///< This variable is set to true if a problem is encountered while parsing the FLV. + +/// Checks a FLV Header for validness. Returns true if the header is valid, false +/// if the header is not. Not valid can mean: +/// - Not starting with the string "FLV". +/// - The DataOffset is not 9 bytes. +/// - The PreviousTagSize is not 0 bytes. +/// +/// Note that we see PreviousTagSize as part of the FLV header, not part of the tag header! +bool FLV::check_header(char * header){ + if (header[0] != 'F') return false; + if (header[1] != 'L') return false; + if (header[2] != 'V') return false; + if (header[5] != 0) return false; + if (header[6] != 0) return false; + if (header[7] != 0) return false; + if (header[8] != 0x09) return false; + if (header[9] != 0) return false; + if (header[10] != 0) return false; + if (header[11] != 0) return false; + if (header[12] != 0) return false; + return true; +}//FLV::check_header + +/// Checks the first 3 bytes for the string "FLV". Implementing a basic FLV header check, +/// returning true if it is, false if not. +bool FLV::is_header(char * header){ + if (header[0] != 'F') return false; + if (header[1] != 'L') return false; + if (header[2] != 'V') return false; + return true; +}//FLV::is_header + + +/// Returns a std::string describing the tag in detail. +/// The string includes information about whether the tag is +/// audio, video or metadata, what encoding is used, and the details +/// of the encoding itself. +std::string FLV::Tag::tagType(){ + std::string R = ""; + switch (data[0]){ + case 0x09: + switch (data[11] & 0x0F){ + case 1: R += "JPEG"; break; + case 2: R += "H263"; break; + case 3: R += "ScreenVideo1"; break; + case 4: R += "VP6"; break; + case 5: R += "VP6Alpha"; break; + case 6: R += "ScreenVideo2"; break; + case 7: R += "AVC"; break; + default: R += "unknown"; break; + } + R += " video "; + switch (data[11] & 0xF0){ + case 0x10: R += "keyframe"; break; + case 0x20: R += "iframe"; break; + case 0x30: R += "disposableiframe"; break; + case 0x40: R += "generatedkeyframe"; break; + case 0x50: R += "videoinfo"; break; + } + if ((data[11] & 0x0F) == 7){ + switch (data[12]){ + case 0: R += " header"; break; + case 1: R += " NALU"; break; + case 2: R += " endofsequence"; break; + } + } + break; + case 0x08: + switch (data[11] & 0xF0){ + case 0x00: R += "linear PCM PE"; break; + case 0x10: R += "ADPCM"; break; + case 0x20: R += "MP3"; break; + case 0x30: R += "linear PCM LE"; break; + case 0x40: R += "Nelly16kHz"; break; + case 0x50: R += "Nelly8kHz"; break; + case 0x60: R += "Nelly"; break; + case 0x70: R += "G711A-law"; break; + case 0x80: R += "G711mu-law"; break; + case 0x90: R += "reserved"; break; + case 0xA0: R += "AAC"; break; + case 0xB0: R += "Speex"; break; + case 0xE0: R += "MP38kHz"; break; + case 0xF0: R += "DeviceSpecific"; break; + default: R += "unknown"; break; + } + switch (data[11] & 0x0C){ + case 0x0: R += " 5.5kHz"; break; + case 0x4: R += " 11kHz"; break; + case 0x8: R += " 22kHz"; break; + case 0xC: R += " 44kHz"; break; + } + switch (data[11] & 0x02){ + case 0: R += " 8bit"; break; + case 2: R += " 16bit"; break; + } + switch (data[11] & 0x01){ + case 0: R += " mono"; break; + case 1: R += " stereo"; break; + } + R += " audio"; + if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){ + R += " initdata"; + } + break; + case 0x12: + R += "(meta)data"; + break; + default: + R += "unknown"; + break; + } + return R; +}//FLV::Tag::tagtype + +/// Returns the 32-bit timestamp of this tag. +unsigned int FLV::Tag::tagTime(){ + return (data[4] << 16) + (data[5] << 8) + data[6] + (data[7] << 24); +}//tagTime getter + +/// Sets the 32-bit timestamp of this tag. +void FLV::Tag::tagTime(unsigned int T){ + data[4] = ((T >> 16) & 0xFF); + data[5] = ((T >> 8) & 0xFF); + data[6] = (T & 0xFF); + data[7] = ((T >> 24) & 0xFF); +}//tagTime setter + +/// Constructor for a new, empty, tag. +/// The buffer length is initialized to 0, and later automatically +/// increased if neccesary. +FLV::Tag::Tag(){ + len = 0; buf = 0; data = 0; isKeyframe = false; +}//empty constructor + +/// Copy constructor, copies the contents of an existing tag. +/// The buffer length is initialized to the actual size of the tag +/// that is being copied, and later automaticallt increased if +/// neccesary. +FLV::Tag::Tag(const Tag& O){ + buf = O.len; + len = buf; + if (len > 0){ + data = (char*)malloc(len); + memcpy(data, O.data, len); + }else{ + data = 0; + } + isKeyframe = O.isKeyframe; +}//copy constructor + +/// Assignment operator - works exactly like the copy constructor. +/// This operator checks for self-assignment. +FLV::Tag & FLV::Tag::operator= (const FLV::Tag& O){ + if (this != &O){//no self-assignment + if (data != 0){free(data);} + buf = O.len; + len = buf; + if (len > 0){ + data = (char*)malloc(len); + memcpy(data, O.data, len); + }else{ + data = 0; + } + isKeyframe = O.isKeyframe; + } + return *this; +}//assignment operator + +/// Helper function for FLV::MemLoader. +/// This function will try to read count bytes from data buffer D into buffer. +/// This function should be called repeatedly until true. +/// P and sofar are not the same value, because D may not start with the current tag. +/// \param buffer The target buffer. +/// \param count Amount of bytes to read. +/// \param sofar Current amount read. +/// \param D The location of the data buffer. +/// \param S The size of the data buffer. +/// \param P The current position in the data buffer. Will be updated to reflect new position. +/// \return True if count bytes are read succesfully, false otherwise. +bool FLV::Tag::MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P){ + if (sofar >= count){return true;} + int r = 0; + if (P+(count-sofar) > S){r = S-P;}else{r = count-sofar;} + memcpy(buffer+sofar, D+P, r); + P += r; + sofar += r; + if (sofar >= count){return true;} + return false; +}//Tag::MemReadUntil + + +/// Try to load a tag from a data buffer in memory. +/// This is a stateful function - if fed incorrect data, it will most likely never return true again! +/// While this function returns false, the Tag might not contain valid data. +/// \param D The location of the data buffer. +/// \param S The size of the data buffer. +/// \param P The current position in the data buffer. Will be updated to reflect new position. +/// \return True if a whole tag is succesfully read, false otherwise. +bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ + static bool done = true; + static unsigned int sofar = 0; + if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} + if (done){ + //read a header + if (MemReadUntil(data, 11, sofar, D, S, P)){ + //if its a correct FLV header, throw away and read tag header + if (FLV::is_header(data)){ + if (MemReadUntil(data, 13, sofar, D, S, P)){ + if (FLV::check_header(data)){ + sofar = 0; + memcpy(FLV::Header, data, 13); + }else{FLV::Parse_Error = true; return false;} + } + }else{ + //if a tag header, calculate length and read tag body + len = data[3] + 15; + len += (data[2] << 8); + len += (data[1] << 16); + if (buf < len){data = (char*)realloc(data, len); buf = len;} + if (data[0] > 0x12){FLV::Parse_Error = true; return false;} + done = false; + } + } + }else{ + //read tag body + if (MemReadUntil(data, len, sofar, D, S, P)){ + //calculate keyframeness, next time read header again, return true + if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} + done = true; + sofar = 0; + return true; + } + } + return false; +}//Tag::MemLoader + + +/// Helper function for FLV::SockLoader. +/// This function will try to read count bytes from socket sock into buffer. +/// This function should be called repeatedly until true. +/// \param buffer The target buffer. +/// \param count Amount of bytes to read. +/// \param sofar Current amount read. +/// \param sock Socket to read from. +/// \return True if count bytes are read succesfully, false otherwise. +bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, DDV::Socket & sock){ + if (sofar == count){return true;} + int r = sock.read(buffer + sofar,count-sofar); + if (r < 0){ + if (errno != EWOULDBLOCK){ + FLV::Parse_Error = true; + fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); + } + return false; + } + sofar += r; + if (sofar == count){return true;} + if (sofar > count){ + FLV::Parse_Error = true; + fprintf(stderr, "ReadUntil fail: %s. Read too much. All Hell Broke Loose!\n", strerror(errno)); + } + return false; +}//Tag::SockReadUntil + +/// Try to load a tag from a socket. +/// This is a stateful function - if fed incorrect data, it will most likely never return true again! +/// While this function returns false, the Tag might not contain valid data. +/// \param sock The socket to read from. +/// \return True if a whole tag is succesfully read, false otherwise. +bool FLV::Tag::SockLoader(DDV::Socket sock){ + static bool done = true; + static unsigned int sofar = 0; + if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} + if (done){ + if (SockReadUntil(data, 11, sofar, sock)){ + //if its a correct FLV header, throw away and read tag header + if (FLV::is_header(data)){ + if (SockReadUntil(data, 13, sofar, sock)){ + if (FLV::check_header(data)){ + sofar = 0; + memcpy(FLV::Header, data, 13); + }else{FLV::Parse_Error = true; return false;} + } + }else{ + //if a tag header, calculate length and read tag body + len = data[3] + 15; + len += (data[2] << 8); + len += (data[1] << 16); + if (buf < len){data = (char*)realloc(data, len); buf = len;} + if (data[0] > 0x12){FLV::Parse_Error = true; return false;} + done = false; + } + } + }else{ + //read tag body + if (SockReadUntil(data, len, sofar, sock)){ + //calculate keyframeness, next time read header again, return true + if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} + done = true; + sofar = 0; + return true; + } + } + return false; +}//Tag::SockLoader + +/// Try to load a tag from a socket. +/// This is a stateful function - if fed incorrect data, it will most likely never return true again! +/// While this function returns false, the Tag might not contain valid data. +/// \param sock The socket to read from. +/// \return True if a whole tag is succesfully read, false otherwise. +bool FLV::Tag::SockLoader(int sock){ + return SockLoader(DDV::Socket(sock)); +}//Tag::SockLoader + +/// Helper function for FLV::FileLoader. +/// This function will try to read count bytes from file f into buffer. +/// This function should be called repeatedly until true. +/// \param buffer The target buffer. +/// \param count Amount of bytes to read. +/// \param sofar Current amount read. +/// \param f File to read from. +/// \return True if count bytes are read succesfully, false otherwise. +bool FLV::Tag::FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f){ + if (sofar >= count){return true;} + int r = 0; + r = fread(buffer + sofar,1,count-sofar,f); + if (r < 0){FLV::Parse_Error = true; return false;} + sofar += r; + if (sofar >= count){return true;} + return false; +} + +/// Try to load a tag from a file. +/// This is a stateful function - if fed incorrect data, it will most likely never return true again! +/// While this function returns false, the Tag might not contain valid data. +/// \param f The file to read from. +/// \return True if a whole tag is succesfully read, false otherwise. +bool FLV::Tag::FileLoader(FILE * f){ + int preflags = fcntl(fileno(f), F_GETFL, 0); + int postflags = preflags | O_NONBLOCK; + fcntl(fileno(f), F_SETFL, postflags); + static bool done = true; + static unsigned int sofar = 0; + if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} + + if (done){ + //read a header + if (FileReadUntil(data, 11, sofar, f)){ + //if its a correct FLV header, throw away and read tag header + if (FLV::is_header(data)){ + if (FileReadUntil(data, 13, sofar, f)){ + if (FLV::check_header(data)){ + sofar = 0; + memcpy(FLV::Header, data, 13); + }else{FLV::Parse_Error = true;} + } + }else{ + //if a tag header, calculate length and read tag body + len = data[3] + 15; + len += (data[2] << 8); + len += (data[1] << 16); + if (buf < len){data = (char*)realloc(data, len); buf = len;} + if (data[0] > 0x12){FLV::Parse_Error = true;} + done = false; + } + } + }else{ + //read tag body + if (FileReadUntil(data, len, sofar, f)){ + //calculate keyframeness, next time read header again, return true + if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} + done = true; + sofar = 0; + fcntl(fileno(f), F_SETFL, preflags); + return true; + } + } + fcntl(fileno(f), F_SETFL, preflags); + return false; +}//FLV_GetPacket diff --git a/util/flv_tag.h b/util/flv_tag.h new file mode 100644 index 00000000..d91cabb5 --- /dev/null +++ b/util/flv_tag.h @@ -0,0 +1,40 @@ +#pragma once +#include "ddv_socket.h" +#include + +/// This namespace holds all FLV-parsing related functionality. +namespace FLV { + //variables + extern char Header[13]; ///< Holds the last FLV header parsed. + extern bool Parse_Error; ///< This variable is set to true if a problem is encountered while parsing the FLV. + + //functions + bool check_header(char * header); ///< Checks a FLV Header for validness. + bool is_header(char * header); ///< Checks the first 3 bytes for the string "FLV". + + /// This class is used to hold, work with and get information about a single FLV tag. + class Tag { + public: + int len; ///< Actual length of tag. + bool isKeyframe; ///< True if current tag is a video keyframe. + char * data; ///< Pointer to tag buffer. + std::string tagType(); ///< Returns a std::string describing the tag in detail. + unsigned int tagTime(); ///< Returns the 32-bit timestamp of this tag. + void tagTime(unsigned int T); ///< Sets the 32-bit timestamp of this tag. + Tag(); ///< Constructor for a new, empty, tag. + Tag(const Tag& O); ///< Copy constructor, copies the contents of an existing tag. + Tag & operator= (const Tag& O); ///< Assignment operator - works exactly like the copy constructor. + //loader functions + bool MemLoader(char * D, unsigned int S, unsigned int & P); + bool SockLoader(int sock); + bool SockLoader(DDV::Socket sock); + bool FileLoader(FILE * f); + protected: + int buf; ///< Maximum length of buffer space. + //loader helper functions + bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P); + bool SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, DDV::Socket & sock); + bool FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f); + };//Tag + +};//FLV namespace diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 36557fbd..dd01e9b2 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -1,42 +1,5 @@ -#pragma once -#include "ddv_socket.cpp" -#include -#include -#include - -class HTTPReader{ - public: - HTTPReader(); - bool ReadSocket(int CONN_fd); - bool ReadSocket(FILE * F); - std::string GetHeader(std::string i); - std::string GetVar(std::string i); - void SetHeader(std::string i, std::string v); - void SetHeader(std::string i, int v); - void SetVar(std::string i, std::string v); - void SetBody(std::string s); - void SetBody(char * buffer, int len); - std::string BuildRequest(); - std::string BuildResponse(std::string code, std::string message); - void SendResponse(int conn, std::string code, std::string message); - void SendBodyPart(int conn, char * buffer, int len); - void SendBodyPart(int conn, std::string bodypart); - void Clean(); - bool CleanForNext(); - std::string body; - std::string method; - std::string url; - std::string protocol; - unsigned int length; - private: - bool seenHeaders; - bool seenReq; - bool parse(); - std::string HTTPbuffer; - std::map headers; - std::map vars; - void Trim(std::string & s); -};//HTTPReader +#include "http_parser.h" +#include "ddv_socket.h" HTTPReader::HTTPReader(){Clean();} void HTTPReader::Clean(){ @@ -127,29 +90,28 @@ void HTTPReader::SetVar(std::string i, std::string v){ vars[i] = v; } -bool HTTPReader::ReadSocket(int CONN_fd){ +bool HTTPReader::Read(DDV::Socket & sock){ //returned true als hele http packet gelezen is int r = 0; int b = 0; char buffer[500]; while (true){ - r = DDV_ready(CONN_fd); + r = sock.ready(); if (r < 1){ - if (r == 0){ - socketError = true; + if (r == -1){ #if DEBUG >= 1 fprintf(stderr, "User socket is disconnected.\n"); #endif } return parse(); } - b = DDV_iread(buffer, 500, CONN_fd); + b = sock.iread(buffer, 500); HTTPbuffer.append(buffer, b); } return false; }//HTTPReader::ReadSocket -bool HTTPReader::ReadSocket(FILE * F){ +bool HTTPReader::Read(FILE * F){ //returned true als hele http packet gelezen is int b = 1; char buffer[500]; @@ -210,23 +172,23 @@ bool HTTPReader::parse(){ return false; //we should never get here... }//HTTPReader::parse -void HTTPReader::SendResponse(int conn, std::string code, std::string message){ +void HTTPReader::SendResponse(DDV::Socket & conn, std::string code, std::string message){ std::string tmp = BuildResponse(code, message); - DDV_write(tmp.c_str(), tmp.size(), conn); + conn.write(tmp); } -void HTTPReader::SendBodyPart(int conn, char * buffer, int len){ +void HTTPReader::SendBodyPart(DDV::Socket & conn, char * buffer, int len){ std::string tmp; tmp.append(buffer, len); SendBodyPart(conn, tmp); } -void HTTPReader::SendBodyPart(int conn, std::string bodypart){ +void HTTPReader::SendBodyPart(DDV::Socket & conn, std::string bodypart){ static char len[10]; int sizelen; sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); - DDV_write(len, sizelen, conn); - DDV_write(bodypart.c_str(), bodypart.size(), conn); - DDV_write(len+sizelen-2, 2, conn); + conn.write(len, sizelen); + conn.write(bodypart); + conn.write(len+sizelen-2, 2); } diff --git a/util/http_parser.h b/util/http_parser.h new file mode 100644 index 00000000..2741f0c4 --- /dev/null +++ b/util/http_parser.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include +#include +#include "ddv_socket.h" + +class HTTPReader{ + public: + HTTPReader(); + bool Read(DDV::Socket & sock); + bool Read(FILE * F); + std::string GetHeader(std::string i); + std::string GetVar(std::string i); + void SetHeader(std::string i, std::string v); + void SetHeader(std::string i, int v); + void SetVar(std::string i, std::string v); + void SetBody(std::string s); + void SetBody(char * buffer, int len); + std::string BuildRequest(); + std::string BuildResponse(std::string code, std::string message); + void SendResponse(DDV::Socket & conn, std::string code, std::string message); + void SendBodyPart(DDV::Socket & conn, char * buffer, int len); + void SendBodyPart(DDV::Socket & conn, std::string bodypart); + void Clean(); + bool CleanForNext(); + std::string body; + std::string method; + std::string url; + std::string protocol; + unsigned int length; + private: + bool seenHeaders; + bool seenReq; + bool parse(); + std::string HTTPbuffer; + std::map headers; + std::map vars; + void Trim(std::string & s); +};//HTTPReader + diff --git a/util/server_setup.cpp b/util/server_setup.cpp index fbd93aa0..bd9e9390 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -1,23 +1,21 @@ -int mainHandler(int CONN_fd);//define this function in your own code! #include -#include "ddv_socket.cpp" //DDVTech Socket wrapper -#include "flv_sock.cpp" //FLV parsing with DDVTech Socket wrapper -int server_socket = 0; +#include "ddv_socket.h" //DDVTech Socket wrapper +#include "flv_tag.h" //FLV parsing with DDVTech Socket wrapper +DDV::ServerSocket server_socket(-1); void termination_handler (int signum){ - if (server_socket == 0) return; + if (!server_socket.connected()) return; switch (signum){ case SIGINT: break; case SIGHUP: break; case SIGTERM: break; default: return; break; } - close(server_socket); - server_socket = 0; + server_socket.close(); } int main(int argc, char ** argv){ - int CONN_fd = 0; + DDV::Socket CONN_fd(-1); //setup signal handler struct sigaction new_action; @@ -31,12 +29,14 @@ int main(int argc, char ** argv){ int listen_port = DEFAULT_PORT; bool daemon_mode = true; + std::string interface = "0.0.0.0"; int opt = 0; - static const char *optString = "np:h?"; + static const char *optString = "np:i:h?"; static const struct option longOpts[] = { {"help",0,0,'h'}, {"port",1,0,'p'}, + {"interface",1,0,'i'}, {"no-daemon",0,0,'n'} }; while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ @@ -44,6 +44,9 @@ int main(int argc, char ** argv){ case 'p': listen_port = atoi(optarg); break; + case 'i': + interface = optarg; + break; case 'n': daemon_mode = false; break; @@ -55,11 +58,11 @@ int main(int argc, char ** argv){ } } - server_socket = DDV_Listen(listen_port); + server_socket = DDV::ServerSocket(listen_port, interface); #if DEBUG >= 3 - fprintf(stderr, "Made a listening socket on port %i...\n", listen_port); + fprintf(stderr, "Made a listening socket on %s:%i...\n", interface.c_str(), listen_port); #endif - if (server_socket > 0){ + if (server_socket.connected()){ if (daemon_mode){ daemon(1, 0); #if DEBUG >= 3 @@ -73,22 +76,22 @@ int main(int argc, char ** argv){ return 1; } int status; - while (server_socket > 0){ + while (server_socket.connected()){ waitpid((pid_t)-1, &status, WNOHANG); - CONN_fd = DDV_Accept(server_socket); - if (CONN_fd > 0){ + CONN_fd = server_socket.accept(); + if (CONN_fd.connected()){ pid_t myid = fork(); if (myid == 0){ break; }else{ #if DEBUG >= 3 - fprintf(stderr, "Spawned new process %i for handling socket %i\n", (int)myid, CONN_fd); + fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, CONN_fd.getSocket()); #endif } } } - if (server_socket <= 0){ + if (!server_socket.connected()){ return 0; } - return mainHandler(CONN_fd); + return MAINHANDLER(CONN_fd); } From 1599894b6eddf54b44b3b39a39e8102aabfef75b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 9 Apr 2011 03:24:29 +0200 Subject: [PATCH 02/15] Renamed all binaries with DDV_ prefix, updated Buffer for new systems, updated RAW connector, partly updated RTMP connector - ready for recode. All connectors now have host binding options. --- util/ddv_socket.cpp | 8 ++++++++ util/ddv_socket.h | 1 + 2 files changed, 9 insertions(+) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 2835a5cf..6c5027b7 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -8,6 +8,14 @@ DDV::Socket::Socket(int sockNo){ Blocking = false; }//DDV::Socket 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. +DDV::Socket::Socket(){ + sock = -1; + Error = false; + Blocking = false; +}//DDV::Socket basic constructor + /// Close connection. The internal socket is closed and then set to -1. void DDV::Socket::close(){ #if DEBUG >= 3 diff --git a/util/ddv_socket.h b/util/ddv_socket.h index cc9421ae..298b5954 100644 --- a/util/ddv_socket.h +++ b/util/ddv_socket.h @@ -19,6 +19,7 @@ namespace DDV{ private: int sock; ///< Internally saved socket number. public: + Socket(); ///< Create a new disconnected base socket. Socket(int sockNo); ///< Create a new base socket. Socket(std::string adres, bool nonblock = false); ///< Create a new Unix Socket. bool Error; ///< Set to true if a socket error happened. From 69b1f859721e4002394bc7945dd3f189e36c78db Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 10 Apr 2011 20:02:04 +0200 Subject: [PATCH 03/15] RAW connector cleanup, added configfile support, added setuid support, added some more comments other places --- util/ddv_socket.cpp | 2 + util/server_setup.cpp | 142 ++++++++++++++++++++++++++++++++---------- 2 files changed, 110 insertions(+), 34 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 6c5027b7..76c2ee8a 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -309,6 +309,8 @@ DDV::ServerSocket::ServerSocket(std::string address, bool nonblock){ DDV::Socket DDV::ServerSocket::accept(bool nonblock){ if (sock < 0){return DDV::Socket(-1);} int r = ::accept(sock, 0, 0); + //set the socket to be nonblocking, if requested. + //we could do this through accept4 with a flag, but that call is non-standard... if ((r >= 0) && nonblock){ int flags = fcntl(r, F_GETFL, 0); flags |= O_NONBLOCK; diff --git a/util/server_setup.cpp b/util/server_setup.cpp index bd9e9390..daa9e6c6 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -1,9 +1,18 @@ #include #include "ddv_socket.h" //DDVTech Socket wrapper #include "flv_tag.h" //FLV parsing with DDVTech Socket wrapper +#include +#include +#include +#define defstr(x) #x //converts a define name to string +#define defstrh(x) "[" defstr(x) "]" //converts define name to [string] DDV::ServerSocket server_socket(-1); -void termination_handler (int signum){ +/// Basic signal handler. Disconnects the server_socket if it receives +/// a SIGINT, SIGHUP or SIGTERM signal, but does nothing for SIGPIPE. +/// Disconnecting the server_socket will terminate the main listening loop +/// and cleanly shut down the process. +void signal_handler (int signum){ if (!server_socket.connected()) return; switch (signum){ case SIGINT: break; @@ -12,57 +21,104 @@ void termination_handler (int signum){ default: return; break; } server_socket.close(); -} +}//signal_handler +/// Generic main entry point and loop for DDV Connectors. +/// This sets up the proper termination handler, checks commandline options, +/// parses config files and opens a listening socket on the requested port. +/// Any incoming connections will be accepted and start up the function MAINHANDLER, +/// which should be #defined before including server_setup.cpp. +/// The default port is set by #define DEFAULT_PORT. +/// The configuration file section is set by #define CONFIGSECT. int main(int argc, char ** argv){ - DDV::Socket CONN_fd(-1); - + DDV::Socket S;//placeholder for incoming connections + //setup signal handler struct sigaction new_action; - new_action.sa_handler = termination_handler; + new_action.sa_handler = signal_handler; sigemptyset (&new_action.sa_mask); new_action.sa_flags = 0; sigaction(SIGINT, &new_action, NULL); sigaction(SIGHUP, &new_action, NULL); sigaction(SIGTERM, &new_action, NULL); sigaction(SIGPIPE, &new_action, NULL); - + + //default values int listen_port = DEFAULT_PORT; bool daemon_mode = true; std::string interface = "0.0.0.0"; + std::string configfile = "/etc/ddvtech.conf"; + std::string username = "root"; + bool ignore_daemon = false; + bool ignore_interface = false; + bool ignore_port = false; + bool ignore_user = false; int opt = 0; - static const char *optString = "np:i:h?"; + static const char *optString = "ndp:i:u:c:h?"; static const struct option longOpts[] = { {"help",0,0,'h'}, {"port",1,0,'p'}, {"interface",1,0,'i'}, - {"no-daemon",0,0,'n'} + {"username",1,0,'u'}, + {"no-daemon",0,0,'n'}, + {"daemon",0,0,'d'}, + {"configfile",1,0,'c'} }; while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ switch (opt){ - case 'p': - listen_port = atoi(optarg); - break; - case 'i': - interface = optarg; - break; - case 'n': - daemon_mode = false; - break; + case 'p': listen_port = atoi(optarg); ignore_port = true; break; + case 'i': interface = optarg; ignore_interface = true; break; + case 'n': daemon_mode = false; ignore_daemon = true; break; + case 'd': daemon_mode = true; ignore_daemon = true; break; + case 'c': configfile = optarg; break; + case 'u': username = optarg; ignore_user = true; break; case 'h': case '?': - printf("Options: -h[elp], -?, -n[o-daemon], -p[ort] #\n"); + printf("Options: -h[elp], -?, -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n"); + printf("Defaults:\n interface: 0.0.0.0\n port: %i\n daemon mode: true\n configfile: /etc/ddvtech.conf\n username: root\n", listen_port); + printf("Username root means no change to UID, no matter what the UID is.\n"); + printf("If the configfile exists, it is always loaded first. Commandline settings then overwrite the config file.\n"); + printf("\nThis process takes it directives from the %s section of the configfile.\n", defstrh(CONFIGSECT)); return 1; break; } - } - + }//commandline options parser + + std::ifstream conf(configfile.c_str(), std::ifstream::in); + std::string tmpstr; + bool acc_comm = false; + size_t foundeq; + if (conf.fail()){ + #if DEBUG >= 3 + fprintf(stderr, "Configuration file %s not found - using build-in defaults...\n", configfile.c_str()); + #endif + }else{ + while (conf.good()){ + getline(conf, tmpstr); + if (tmpstr[0] == '['){//new section? check if we care. + if (tmpstr == defstrh(CONFIGSECT)){acc_comm = true;}else{acc_comm = false;} + }else{ + if (!acc_comm){break;}//skip all lines in this section if we do not care about it + foundeq = tmpstr.find('='); + if (foundeq != std::string::npos){ + if ((tmpstr.substr(0, foundeq) == "port") && !ignore_port){listen_port = atoi(tmpstr.substr(foundeq+1).c_str());} + if ((tmpstr.substr(0, foundeq) == "interface") && !ignore_interface){interface = tmpstr.substr(foundeq+1);} + if ((tmpstr.substr(0, foundeq) == "username") && !ignore_user){username = tmpstr.substr(foundeq+1);} + if ((tmpstr.substr(0, foundeq) == "daemon") && !ignore_daemon){daemon_mode = true;} + if ((tmpstr.substr(0, foundeq) == "nodaemon") && !ignore_daemon){daemon_mode = false;} + }//found equals sign + }//section contents + }//configfile line loop + }//configuration + + //setup a new server socket, for the correct interface and port server_socket = DDV::ServerSocket(listen_port, interface); #if DEBUG >= 3 fprintf(stderr, "Made a listening socket on %s:%i...\n", interface.c_str(), listen_port); #endif if (server_socket.connected()){ + //if setup success, enter daemon mode if requested if (daemon_mode){ daemon(1, 0); #if DEBUG >= 3 @@ -75,23 +131,41 @@ int main(int argc, char ** argv){ #endif return 1; } - int status; - while (server_socket.connected()){ - waitpid((pid_t)-1, &status, WNOHANG); - CONN_fd = server_socket.accept(); - if (CONN_fd.connected()){ - pid_t myid = fork(); - if (myid == 0){ - break; + + if (username != "root"){ + struct passwd * user_info = getpwnam(username.c_str()); + if (!user_info){ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str()); + #endif + return 1; + }else{ + if (setuid(user_info->pw_uid) != 0){ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str()); + #endif }else{ #if DEBUG >= 3 - fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, CONN_fd.getSocket()); + fprintf(stderr, "Changed user to %s\n", username.c_str()); #endif } } } - if (!server_socket.connected()){ - return 0; - } - return MAINHANDLER(CONN_fd); -} + + int status; + while (server_socket.connected()){ + while (waitpid((pid_t)-1, &status, WNOHANG) > 0){}//clean up all child processes + S = server_socket.accept(); + if (S.connected()){//check if the new connection is valid + pid_t myid = fork(); + if (myid == 0){//if new child, start MAINHANDLER + return MAINHANDLER(S); + }else{//otherwise, do nothing or output debugging text + #if DEBUG >= 3 + fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket()); + #endif + } + } + }//while connected + return 0; +}//main From e416612a3337ef5b2e74d6764ba03c34815b8e9f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 10 Apr 2011 22:25:51 +0200 Subject: [PATCH 04/15] Documented and nicified AMF parsing. Updated all existing code for this change, except for RTMP connector (which gets a rewrite, like, today). --- util/amf.cpp | 580 +++++++++++++++++++++++++-------------------------- util/amf.h | 68 ++++++ 2 files changed, 358 insertions(+), 290 deletions(-) create mode 100644 util/amf.h diff --git a/util/amf.cpp b/util/amf.cpp index a600d12a..fdff660f 100644 --- a/util/amf.cpp +++ b/util/amf.cpp @@ -1,264 +1,256 @@ -#include -#include -#include +#include "amf.h" -#define AMF0_NUMBER 0x00 -#define AMF0_BOOL 0x01 -#define AMF0_STRING 0x02 -#define AMF0_OBJECT 0x03 -#define AMF0_MOVIECLIP 0x04 -#define AMF0_NULL 0x05 -#define AMF0_UNDEFINED 0x06 -#define AMF0_REFERENCE 0x07 -#define AMF0_ECMA_ARRAY 0x08 -#define AMF0_OBJ_END 0x09 -#define AMF0_STRICT_ARRAY 0x0A -#define AMF0_DATE 0x0B -#define AMF0_LONGSTRING 0x0C -#define AMF0_UNSUPPORTED 0x0D -#define AMF0_RECORDSET 0x0E -#define AMF0_XMLDOC 0x0F -#define AMF0_TYPED_OBJ 0x10 -#define AMF0_UPGRADE 0x11 -#define AMF0_DDV_CONTAINER 0xFF +/// Returns the std::string Indice for the current object, if available. +/// Returns an empty string if no indice exists. +std::string AMF::Object::Indice(){return myIndice;}; -class AMFType { - public: - std::string Indice(){return myIndice;}; - unsigned char GetType(){return myType;}; - double NumValue(){return numval;}; - std::string StrValue(){return strval;}; - const char * Str(){return strval.c_str();}; - int hasContent(){ - if (!contents){return 0;} - return contents->size(); - }; - void addContent(AMFType c){if (contents != 0){contents->push_back(c);}}; - AMFType* getContentP(int i){if (contents != 0){return &contents->at(i);}else{return 0;}}; - AMFType getContent(int i){if (contents != 0){return contents->at(i);}else{return AMFType("error");}}; - AMFType* getContentP(std::string s){ - if (contents != 0){ - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - if (it->Indice() == s){ - return &(*it); - } +/// Returns the AMF::obj0type AMF0 object type for this object. +AMF::obj0type AMF::Object::GetType(){return myType;}; + +/// Returns the numeric value of this object, if available. +/// If this object holds no numeric value, 0 is returned. +double AMF::Object::NumValue(){return numval;}; + +/// Returns the std::string value of this object, if available. +/// If this object holds no string value, an empty string is returned. +std::string AMF::Object::StrValue(){return strval;}; + +/// Returns the C-string value of this object, if available. +/// If this object holds no string value, an empty C-string is returned. +const char * AMF::Object::Str(){return strval.c_str();}; + +/// Returns a count of the amount of objects this object currently holds. +/// If this object is not a container type, this function will always return 0. +int AMF::Object::hasContent(){return contents.size();}; + +/// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types. +void AMF::Object::addContent(AMF::Object c){contents.push_back(c);}; + +/// Returns a pointer to the object held at indice i. +/// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +AMF::Object* AMF::Object::getContentP(int i){return &contents.at(i);}; + +/// Returns a copy of the object held at indice i. +/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +AMF::Object AMF::Object::getContent(int i){return contents.at(i);}; + +/// Returns a pointer to the object held at indice s. +/// Returns NULL if no object is held at this indice. +/// \param s The indice of the object in this container. +AMF::Object* AMF::Object::getContentP(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return &(*it);} + } + return this; +}; + +/// Returns a copy of the object held at indice s. +/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param s The indice of the object in this container. +AMF::Object AMF::Object::getContent(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return *it;} + } + return AMF::Object("error", AMF0_DDV_CONTAINER); +}; + +/// Constructor for numeric objects. +/// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The numeric value of this object. Numeric AMF0 objects only support double-type values. +/// \param setType The object type to force this object to. +AMF::Object::Object(std::string indice, double val, AMF::obj0type setType){//num type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = val; +}; + +/// Constructor for string objects. +/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. +/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done automatically. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The string value of this object. +/// \param setType The object type to force this object to. +AMF::Object::Object(std::string indice, std::string val, AMF::obj0type setType){//str type initializer + myIndice = indice; + myType = setType; + strval = val; + numval = 0; +}; + +/// Constructor for container objects. +/// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param setType The object type to force this object to. +AMF::Object::Object(std::string indice, AMF::obj0type setType){//object type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = 0; +}; + +/// Prints the contents of this object to std::cerr. +/// If this object contains other objects, it will call itself recursively +/// and print all nested content in a nice human-readable format. +void AMF::Object::Print(std::string indent){ + std::cerr << indent; + // print my type + switch (myType){ + case AMF::AMF0_NUMBER: std::cerr << "Number"; break; + case AMF::AMF0_BOOL: std::cerr << "Bool"; break; + case AMF::AMF0_STRING://short string + case AMF::AMF0_LONGSTRING: std::cerr << "String"; break; + case AMF::AMF0_OBJECT: std::cerr << "Object"; break; + case AMF::AMF0_MOVIECLIP: std::cerr << "MovieClip"; break; + case AMF::AMF0_NULL: std::cerr << "Null"; break; + case AMF::AMF0_UNDEFINED: std::cerr << "Undefined"; break; + case AMF::AMF0_REFERENCE: std::cerr << "Reference"; break; + case AMF::AMF0_ECMA_ARRAY: std::cerr << "ECMA Array"; break; + case AMF::AMF0_OBJ_END: std::cerr << "Object end"; break; + case AMF::AMF0_STRICT_ARRAY: std::cerr << "Strict Array"; break; + case AMF::AMF0_DATE: std::cerr << "Date"; break; + case AMF::AMF0_UNSUPPORTED: std::cerr << "Unsupported"; break; + case AMF::AMF0_RECORDSET: std::cerr << "Recordset"; break; + case AMF::AMF0_XMLDOC: std::cerr << "XML Document"; break; + case AMF::AMF0_TYPED_OBJ: std::cerr << "Typed Object"; break; + case AMF::AMF0_UPGRADE: std::cerr << "Upgrade to AMF3"; break; + case AMF::AMF0_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; + } + // print my string indice, if available + std::cerr << " " << myIndice << " "; + // print my numeric or string contents + switch (myType){ + case AMF::AMF0_NUMBER: case AMF::AMF0_BOOL: case AMF::AMF0_REFERENCE: case AMF::AMF0_DATE: std::cerr << numval; break; + case AMF::AMF0_STRING: case AMF::AMF0_LONGSTRING: case AMF::AMF0_XMLDOC: case AMF::AMF0_TYPED_OBJ: std::cerr << strval; break; + default: break;//we don't care about the rest, and don't want a compiler warning... + } + std::cerr << std::endl; + // if I hold other objects, print those too, recursively. + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} + } +};//print + +/// Packs the AMF object to a std::string for transfer over the network. +/// If the object is a container type, this function will call itself recursively and contain all contents. +/// Tip: When sending multiple AMF objects in one go, put them in a single AMF::AMF0_DDV_CONTAINER for easy transfer. +std::string AMF::Object::Pack(){ + std::string r = ""; + //check for string/longstring conversion + if ((myType == AMF::AMF0_STRING) && (strval.size() > 0xFFFF)){myType = AMF::AMF0_LONGSTRING;} + //skip output of DDV container types, they do not exist. Only output their contents. + if (myType != AMF::AMF0_DDV_CONTAINER){r += myType;} + //output the properly formatted AMF0 data stream for this object's contents. + switch (myType){ + case AMF::AMF0_NUMBER: + r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); + r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); + r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); + r += *(((char*)&numval)+1); r += *(((char*)&numval)); + break; + case AMF::AMF0_DATE: + r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); + r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); + r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); + r += *(((char*)&numval)+1); r += *(((char*)&numval)); + r += (char)0;//timezone always 0 + r += (char)0;//timezone always 0 + break; + case AMF::AMF0_BOOL: + r += (char)numval; + break; + case AMF::AMF0_STRING: + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case AMF::AMF0_LONGSTRING: + case AMF::AMF0_XMLDOC://is always a longstring + r += strval.size() / (256*256*256); + r += strval.size() / (256*256); + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case AMF::AMF0_TYPED_OBJ: + r += Indice().size() / 256; + r += Indice().size() % 256; + r += Indice(); + //is an object, with the classname first + case AMF::AMF0_OBJECT: + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); } } - return this; - }; - AMFType getContent(std::string s){ - if (contents != 0){ - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - if (it->Indice() == s){ - return *it; - } - } - } - return AMFType("error"); - }; - AMFType(std::string indice, double val, unsigned char setType = AMF0_NUMBER){//num type initializer - myIndice = indice; - myType = setType; - strval = ""; - numval = val; - contents = 0; - }; - AMFType(std::string indice, std::string val, unsigned char setType = AMF0_STRING){//str type initializer - myIndice = indice; - myType = setType; - strval = val; - numval = 0; - contents = 0; - }; - AMFType(std::string indice, unsigned char setType = AMF0_OBJECT){//object type initializer - myIndice = indice; - myType = setType; - strval = ""; - numval = 0; - contents = new std::vector; - }; - ~AMFType(){if (contents != 0){delete contents;contents=0;}}; - AMFType& operator=(const AMFType &a) { - myIndice = a.myIndice; - myType = a.myType; - strval = a.strval; - numval = a.numval; - if (contents){ - if (a.contents != contents){ - delete contents; - if (a.contents){ - contents = new std::vector; - for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ - contents->push_back(*it); - } - }else{ - contents = 0; - } + r += (char)0; r += (char)0; r += (char)9; + break; + case AMF::AMF0_MOVIECLIP: + case AMF::AMF0_OBJ_END: + case AMF::AMF0_UPGRADE: + case AMF::AMF0_NULL: + case AMF::AMF0_UNDEFINED: + case AMF::AMF0_RECORDSET: + case AMF::AMF0_UNSUPPORTED: + //no data to add + break; + case AMF::AMF0_REFERENCE: + r += (char)((int)numval / 256); + r += (char)((int)numval % 256); + break; + case AMF::AMF0_ECMA_ARRAY:{ + int arrlen = 0; + if (contents.size() > 0){ + arrlen = contents.size(); + r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); } }else{ - if (a.contents){ - contents = new std::vector; - for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ - contents->push_back(*it); - } + r += (char)0; r += (char)0; r += (char)0; r += (char)0; + } + r += (char)0; r += (char)0; r += (char)9; + } break; + case AMF::AMF0_STRICT_ARRAY:{ + int arrlen = 0; + if (contents.size() > 0){ + arrlen = contents.size(); + r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Pack(); + } + }else{ + r += (char)0; r += (char)0; r += (char)0; r += (char)0; + } + } break; + case AMF::AMF0_DDV_CONTAINER://only send contents + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Pack(); } } - return *this; - };//= operator - AMFType(const AMFType &a){ - myIndice = a.myIndice; - myType = a.myType; - strval = a.strval; - numval = a.numval; - if (a.contents){ - contents = new std::vector; - for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ - contents->push_back(*it); - } - }else{contents = 0;} - };//copy constructor - void Print(std::string indent = ""){ - std::cerr << indent; - switch (myType){ - case AMF0_NUMBER: std::cerr << "Number"; break; - case AMF0_BOOL: std::cerr << "Bool"; break; - case AMF0_STRING://short string - case AMF0_LONGSTRING: std::cerr << "String"; break; - case AMF0_OBJECT: std::cerr << "Object"; break; - case AMF0_MOVIECLIP: std::cerr << "MovieClip"; break; - case AMF0_NULL: std::cerr << "Null"; break; - case AMF0_UNDEFINED: std::cerr << "Undefined"; break; - case AMF0_REFERENCE: std::cerr << "Reference"; break; - case AMF0_ECMA_ARRAY: std::cerr << "ECMA Array"; break; - case AMF0_OBJ_END: std::cerr << "Object end"; break; - case AMF0_STRICT_ARRAY: std::cerr << "Strict Array"; break; - case AMF0_DATE: std::cerr << "Date"; break; - case AMF0_UNSUPPORTED: std::cerr << "Unsupported"; break; - case AMF0_RECORDSET: std::cerr << "Recordset"; break; - case AMF0_XMLDOC: std::cerr << "XML Document"; break; - case AMF0_TYPED_OBJ: std::cerr << "Typed Object"; break; - case AMF0_UPGRADE: std::cerr << "Upgrade to AMF3"; break; - case AMF0_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; - } - std::cerr << " " << myIndice << " "; - switch (myType){ - case AMF0_NUMBER: case AMF0_BOOL: case AMF0_REFERENCE: case AMF0_DATE: std::cerr << numval; break; - case AMF0_STRING: case AMF0_LONGSTRING: case AMF0_XMLDOC: case AMF0_TYPED_OBJ: std::cerr << strval; break; - } - std::cerr << std::endl; - if (contents){ - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){it->Print(indent+" ");} - } - };//print - std::string Pack(){ - std::string r = ""; - if ((myType == AMF0_STRING) && (strval.size() > 0xFFFF)){myType = AMF0_LONGSTRING;} - if (myType != AMF0_DDV_CONTAINER){r += myType;} - switch (myType){ - case AMF0_NUMBER: - r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); - r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); - r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); - r += *(((char*)&numval)+1); r += *(((char*)&numval)); - break; - case AMF0_DATE: - r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); - r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); - r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); - r += *(((char*)&numval)+1); r += *(((char*)&numval)); - r += (char)0;//timezone always 0 - r += (char)0;//timezone always 0 - break; - case AMF0_BOOL: - r += (char)numval; - break; - case AMF0_STRING: - r += strval.size() / 256; - r += strval.size() % 256; - r += strval; - break; - case AMF0_LONGSTRING: - case AMF0_XMLDOC://is always a longstring - r += strval.size() / (256*256*256); - r += strval.size() / (256*256); - r += strval.size() / 256; - r += strval.size() % 256; - r += strval; - break; - case AMF0_TYPED_OBJ: - r += Indice().size() / 256; - r += Indice().size() % 256; - r += Indice(); - //is an object, with the classname first - case AMF0_OBJECT: - if (contents){ - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - r += it->Indice().size() / 256; - r += it->Indice().size() % 256; - r += it->Indice(); - r += it->Pack(); - } - } - r += (char)0; r += (char)0; r += (char)9; - break; - case AMF0_MOVIECLIP: - case AMF0_NULL: - case AMF0_UNDEFINED: - case AMF0_RECORDSET: - case AMF0_UNSUPPORTED: - //no data to add - break; - case AMF0_REFERENCE: - r += (char)((int)numval / 256); - r += (char)((int)numval % 256); - break; - case AMF0_ECMA_ARRAY:{ - int arrlen = 0; - if (contents){ - arrlen = contents->size(); - r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - r += it->Indice().size() / 256; - r += it->Indice().size() % 256; - r += it->Indice(); - r += it->Pack(); - } - }else{ - r += (char)0; r += (char)0; r += (char)0; r += (char)0; - } - r += (char)0; r += (char)0; r += (char)9; - } break; - case AMF0_STRICT_ARRAY:{ - int arrlen = 0; - if (contents){ - arrlen = contents->size(); - r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - r += it->Pack(); - } - }else{ - r += (char)0; r += (char)0; r += (char)0; r += (char)0; - } - } break; - case AMF0_DDV_CONTAINER://only send contents - if (contents){ - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - r += it->Pack(); - } - } - break; - } - return r; - };//pack - protected: - std::string myIndice; - unsigned char myType; - std::string strval; - double numval; - std::vector * contents; -};//AMFType + break; + } + return r; +};//pack -AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ +/// Parses a single AMF0 type - used recursively by the AMF::parse() functions. +/// This function updates i every call with the new position in the data. +/// \param data The raw data to parse. +/// \param len The size of the raw data. +/// \param i Current parsing position in the raw data. +/// \param name Indice name for any new object created. +/// \returns A single AMF::Object, parsed from the raw data. +AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ std::string tmpstr; unsigned int tmpi = 0; unsigned char tmpdbl[8]; @@ -266,7 +258,7 @@ AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); #endif switch (data[i]){ - case AMF0_NUMBER: + case AMF::AMF0_NUMBER: tmpdbl[7] = data[i+1]; tmpdbl[6] = data[i+2]; tmpdbl[5] = data[i+3]; @@ -276,9 +268,9 @@ AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int tmpdbl[1] = data[i+7]; tmpdbl[0] = data[i+8]; i+=9;//skip 8(a double)+1 forwards - return AMFType(name, *(double*)tmpdbl, AMF0_NUMBER); + return AMF::Object(name, *(double*)tmpdbl, AMF::AMF0_NUMBER); break; - case AMF0_DATE: + case AMF::AMF0_DATE: tmpdbl[7] = data[i+1]; tmpdbl[6] = data[i+2]; tmpdbl[5] = data[i+3]; @@ -288,97 +280,97 @@ AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int tmpdbl[1] = data[i+7]; tmpdbl[0] = data[i+8]; i+=11;//skip 8(a double)+1+timezone(2) forwards - return AMFType(name, *(double*)tmpdbl, AMF0_DATE); + return AMF::Object(name, *(double*)tmpdbl, AMF::AMF0_DATE); break; - case AMF0_BOOL: + case AMF::AMF0_BOOL: i+=2;//skip bool+1 forwards if (data[i-1] == 0){ - return AMFType(name, (double)0, AMF0_BOOL); + return AMF::Object(name, (double)0, AMF::AMF0_BOOL); }else{ - return AMFType(name, (double)1, AMF0_BOOL); + return AMF::Object(name, (double)1, AMF::AMF0_BOOL); } break; - case AMF0_REFERENCE: + case AMF::AMF0_REFERENCE: tmpi = data[i+1]*256+data[i+2];//get the ref number value as a double i+=3;//skip ref+1 forwards - return AMFType(name, (double)tmpi, AMF0_REFERENCE); + return AMF::Object(name, (double)tmpi, AMF::AMF0_REFERENCE); break; - case AMF0_XMLDOC: + case AMF::AMF0_XMLDOC: tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data i += tmpi + 5;//skip length+size+1 forwards - return AMFType(name, tmpstr, AMF0_XMLDOC); + return AMF::Object(name, tmpstr, AMF::AMF0_XMLDOC); break; - case AMF0_LONGSTRING: + case AMF::AMF0_LONGSTRING: tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data i += tmpi + 5;//skip length+size+1 forwards - return AMFType(name, tmpstr, AMF0_LONGSTRING); + return AMF::Object(name, tmpstr, AMF::AMF0_LONGSTRING); break; - case AMF0_STRING: + case AMF::AMF0_STRING: tmpi = data[i+1]*256+data[i+2];//set tmpi to UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char *)data+i+3, (size_t)tmpi);//add the string data i += tmpi + 3;//skip length+size+1 forwards - return AMFType(name, tmpstr, AMF0_STRING); + return AMF::Object(name, tmpstr, AMF::AMF0_STRING); break; - case AMF0_NULL: - case AMF0_UNDEFINED: - case AMF0_UNSUPPORTED: + case AMF::AMF0_NULL: + case AMF::AMF0_UNDEFINED: + case AMF::AMF0_UNSUPPORTED: ++i; - return AMFType(name, (double)0, data[i-1]); + return AMF::Object(name, (double)0, (AMF::obj0type)data[i-1]); break; - case AMF0_OBJECT:{ + case AMF::AMF0_OBJECT:{ ++i; - AMFType ret = AMFType(name, (unsigned char)AMF0_OBJECT); + AMF::Object ret(name, AMF::AMF0_OBJECT); while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data i += tmpi + 2;//skip length+size forwards - ret.addContent(parseOneAMF(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + ret.addContent(AMF::parseOne(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr } i += 3;//skip 0x000009 return ret; } break; - case AMF0_TYPED_OBJ:{ + case AMF::AMF0_TYPED_OBJ:{ ++i; tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data - AMFType ret = AMFType(tmpstr, (unsigned char)AMF0_TYPED_OBJ);//the object is not named "name" but tmpstr + AMF::Object ret(tmpstr, AMF::AMF0_TYPED_OBJ);//the object is not named "name" but tmpstr while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data i += tmpi + 2;//skip length+size forwards - ret.addContent(parseOneAMF(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + ret.addContent(AMF::parseOne(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr } i += 3;//skip 0x000009 return ret; } break; - case AMF0_ECMA_ARRAY:{ + case AMF::AMF0_ECMA_ARRAY:{ ++i; - AMFType ret = AMFType(name, (unsigned char)AMF0_ECMA_ARRAY); + AMF::Object ret(name, AMF::AMF0_ECMA_ARRAY); i += 4;//ignore the array length, we re-calculate it while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data i += tmpi + 2;//skip length+size forwards - ret.addContent(parseOneAMF(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + ret.addContent(AMF::parseOne(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr } i += 3;//skip 0x000009 return ret; } break; - case AMF0_STRICT_ARRAY:{ - AMFType ret = AMFType(name, (unsigned char)AMF0_STRICT_ARRAY); + case AMF::AMF0_STRICT_ARRAY:{ + AMF::Object ret(name, AMF::AMF0_STRICT_ARRAY); tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to array length i += 5;//skip size+1 forwards while (tmpi > 0){//while not done parsing array - ret.addContent(parseOneAMF(data, len, i, "arrVal"));//add content, recursively parsed, updating i + ret.addContent(AMF::parseOne(data, len, i, "arrVal"));//add content, recursively parsed, updating i --tmpi; } return ret; @@ -387,17 +379,25 @@ AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int #if DEBUG >= 2 fprintf(stderr, "Error: Unimplemented AMF type %hhx - returning.\n", data[i]); #endif - return AMFType("error", (unsigned char)0xFF); -}//parseOneAMF + return AMF::Object("error", AMF::AMF0_DDV_CONTAINER); +}//parseOne -AMFType parseAMF(const unsigned char * data, unsigned int len){ - AMFType ret("returned", (unsigned char)0xFF);//container type +/// Parses a C-string to a valid AMF::Object. +/// This function will find all AMF objects in the string and return +/// them all packed in a single AMF::AMF0_DDV_CONTAINER AMF::Object. +AMF::Object AMF::parse(const unsigned char * data, unsigned int len){ + AMF::Object ret("returned", AMF::AMF0_DDV_CONTAINER);//container type unsigned int i = 0, j = 0; while (i < len){ - ret.addContent(parseOneAMF(data, len, i, "")); + ret.addContent(AMF::parseOne(data, len, i, "")); if (i > j){j = i;}else{return ret;} } return ret; -}//parseAMF -AMFType parseAMF(std::string data){return parseAMF((const unsigned char*)data.c_str(), data.size());} +}//parse +/// Parses a std::string to a valid AMF::Object. +/// This function will find all AMF objects in the string and return +/// them all packed in a single AMF::AMF0_DDV_CONTAINER AMF::Object. +AMF::Object AMF::parse(std::string data){ + return AMF::parse((const unsigned char*)data.c_str(), data.size()); +}//parse diff --git a/util/amf.h b/util/amf.h new file mode 100644 index 00000000..81ab9881 --- /dev/null +++ b/util/amf.h @@ -0,0 +1,68 @@ +#pragma once +#include +#include +//#include +#include + +/// Holds all AMF parsing and creation related functions and classes. +namespace AMF{ + + /// Enumerates all possible AMF0 types, adding a special DDVTECH container type for ease of use. + enum obj0type { + AMF0_NUMBER = 0x00, + AMF0_BOOL = 0x01, + AMF0_STRING = 0x02, + AMF0_OBJECT = 0x03, + AMF0_MOVIECLIP = 0x04, + AMF0_NULL = 0x05, + AMF0_UNDEFINED = 0x06, + AMF0_REFERENCE = 0x07, + AMF0_ECMA_ARRAY = 0x08, + AMF0_OBJ_END = 0x09, + AMF0_STRICT_ARRAY = 0x0A, + AMF0_DATE = 0x0B, + AMF0_LONGSTRING = 0x0C, + AMF0_UNSUPPORTED = 0x0D, + AMF0_RECORDSET = 0x0E, + AMF0_XMLDOC = 0x0F, + AMF0_TYPED_OBJ = 0x10, + AMF0_UPGRADE = 0x11, + AMF0_DDV_CONTAINER = 0xFF + }; + + /// Recursive class that holds AMF0 objects. + /// It supports all AMF0 types (defined in AMF::obj0type), adding support for a special DDVTECH container type. + class Object { + public: + std::string Indice(); + obj0type GetType(); + double NumValue(); + std::string StrValue(); + const char * Str(); + int hasContent(); + void addContent(AMF::Object c); + Object* getContentP(int i); + Object getContent(int i); + Object* getContentP(std::string s); + Object getContent(std::string s); + Object(std::string indice, double val, obj0type setType = AMF0_NUMBER); + Object(std::string indice, std::string val, obj0type setType = AMF0_STRING); + Object(std::string indice, obj0type setType = AMF0_OBJECT); + void Print(std::string indent = ""); + std::string Pack(); + protected: + std::string myIndice; ///< Holds this objects indice, if any. + obj0type myType; ///< Holds this objects AMF0 type. + std::string strval; ///< Holds this objects string value, if any. + double numval; ///< Holds this objects numeric value, if any. + std::vector contents; ///< Holds this objects contents, if any (for container types). + };//AMFType + + /// Parses a C-string to a valid AMF::Object. + Object parse(const unsigned char * data, unsigned int len); + /// Parses a std::string to a valid AMF::Object. + Object parse(std::string data); + /// Parses a single AMF0 type - used recursively by the AMF::parse() functions. + Object parseOne(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); + +};//AMF namespace From 23e1cf43356e429eaf3b669b115f86ef332430ef Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 10 Apr 2011 22:32:40 +0200 Subject: [PATCH 05/15] SWSocket library niet meer nodig! Jeej! --- sockets/SocketW.h | 22 -- sockets/sw_base.cpp | 764 ------------------------------------------ sockets/sw_base.h | 205 ------------ sockets/sw_inet.cpp | 249 -------------- sockets/sw_inet.h | 48 --- sockets/sw_internal.h | 75 ----- sockets/sw_unix.cpp | 99 ------ sockets/sw_unix.h | 41 --- 8 files changed, 1503 deletions(-) delete mode 100644 sockets/SocketW.h delete mode 100644 sockets/sw_base.cpp delete mode 100644 sockets/sw_base.h delete mode 100644 sockets/sw_inet.cpp delete mode 100644 sockets/sw_inet.h delete mode 100644 sockets/sw_internal.h delete mode 100644 sockets/sw_unix.cpp delete mode 100644 sockets/sw_unix.h diff --git a/sockets/SocketW.h b/sockets/SocketW.h deleted file mode 100644 index b7096c0c..00000000 --- a/sockets/SocketW.h +++ /dev/null @@ -1,22 +0,0 @@ -// C++ Socket Wrapper -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindstr�m - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#ifndef SocketW_H -#define SocketW_H - -#include "sw_base.h" -#include "sw_unix.h" -#include "sw_inet.h" - -#endif //SocketW_H diff --git a/sockets/sw_base.cpp b/sockets/sw_base.cpp deleted file mode 100644 index 23ca1eb6..00000000 --- a/sockets/sw_base.cpp +++ /dev/null @@ -1,764 +0,0 @@ -// C++ Socket Wrapper -// SocketW base class -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindstr�m - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#include "sw_base.h" -#include -#include -#include -#include -#include - -#ifndef __WIN32__ - #include - #include - #include - #include - #include - - #define INVALID_SOCKET -1 //avoid M$ braindamage -#else - //why use POSIX standards when we can make our own - //and frustrate people? (well known M$ policy) - - #ifndef EBADF - #define EBADF WSAEBADF - #endif - - #define ENOTSOCK WSAENOTSOCK - #define EOPNOTSUPP WSAEOPNOTSUPP - #define EADDRINUSE WSAEADDRINUSE - #define EWOULDBLOCK WSAEWOULDBLOCK - #define EMSGSIZE WSAEMSGSIZE - #define EINPROGRESS WSAEINPROGRESS - #define EALREADY WSAEALREADY - #define ECONNREFUSED WSAECONNREFUSED - #define ETIMEDOUT WSAETIMEDOUT - #define ENOTCONN WSAENOTCONN - - #ifndef EINTR - #define EINTR WSAEINTR - #endif -#endif - -#ifndef MSG_NOSIGNAL - #define MSG_NOSIGNAL 0 -#endif - -// Socklen hack -#if defined(__linux__) || defined(__FreeBSD__) // || defined(__bsdi__) || defined(__NetBSD__) too, perhaps? Bugreports, please! - #define sw_socklen_t socklen_t -#elif defined(__WIN32__) || defined(__osf__) - #define sw_socklen_t int -#else - #define sw_socklen_t unsigned int -#endif - - -using namespace std; - -#ifdef __WIN32__ -//Win32 braindamage -int close(int fd) -{ - return closesocket(fd); -} - -int fcntl(int fd, int cmd, long arg) -{ - unsigned long mode = arg; - - return WSAIoctl(fd, cmd, &mode, sizeof(unsigned long), NULL, 0, NULL, NULL, NULL); -} - -void WSA_exit(void) -{ - WSACleanup(); -} -#endif - - -//==================================================================== -//== Error handling mode -//==================================================================== -bool sw_DoThrow = false; -bool sw_Verbose = true; - -void sw_setThrowMode(bool throw_errors) -{ - sw_DoThrow = throw_errors; -} - -void sw_setVerboseMode(bool verbose) -{ - sw_Verbose = verbose; -} - -bool sw_getThrowMode(void) -{ - return sw_DoThrow; -} - -bool sw_getVerboseMode(void) -{ - return sw_Verbose; -} - - -//==================================================================== -//== Base error class -//==================================================================== -SWBaseSocket::SWBaseError::SWBaseError() -{ - be = ok; - error_string = ""; - failed_class = NULL; -} - -SWBaseSocket::SWBaseError::SWBaseError(base_error e) -{ - be = e; - error_string = ""; - failed_class = NULL; -} - -string SWBaseSocket::SWBaseError::get_error() -{ - return error_string; -} - -SWBaseSocket* SWBaseSocket::SWBaseError::get_failedClass(void) -{ - return failed_class; -} - -void SWBaseSocket::SWBaseError::set_errorString(string msg) -{ - error_string = msg; -} - -void SWBaseSocket::SWBaseError::set_failedClass(SWBaseSocket *pnt) -{ - failed_class = pnt; -} - -bool SWBaseSocket::SWBaseError::operator==(SWBaseError e) -{ - return be == e.be; -} - -bool SWBaseSocket::SWBaseError::operator!=(SWBaseError e) -{ - return be != e.be; -} - - -//==================================================================== -//== SWBaseSocket -//== Base class for sockets -//==================================================================== -SWBaseSocket::SWBaseSocket() -{ - //indicate nonopen - myfd = -1; - recv_close = false; - - //init values - error_string = ""; - block_mode = blocking; - fsend_ready = true; - frecv_ready = true; - tsec = 0; - tusec = 0; - - #ifdef __WIN32__ - //kick winsock awake - static bool firstuse = true; - if( firstuse == true ){ - WSAData wsaData; - int nCode; - if( (nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0 ){ - handle_errno(NULL, "SWBaseSocket - WSAStartup() failed: "); - exit(-1); // Should never happend - } - - //cleanup at exit - atexit(WSA_exit); - firstuse = false; - } - #endif /* __WIN32__ */ -} - -SWBaseSocket::~SWBaseSocket() -{ - if(myfd > 0) - close(myfd); -} - -bool SWBaseSocket::listen(int qLimit, SWBaseError *error) -{ - get_socket(); - - //Avoid "Address already in use" thingie - char yes=1; - setsockopt(myfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); - - if(::listen(myfd, qLimit) == -1){ - handle_errno(error, "SWBaseSocket::listen() error: "); - return false; - } - - no_error(error); - return true; -} - -SWBaseSocket* SWBaseSocket::accept(SWBaseError *error) -{ - int remotefd = -1; - sockaddr remoteAdr; - - if( !waitRead(error) ) - return NULL; - - sw_socklen_t ssize = sizeof(sockaddr); - - if((remotefd = ::accept(myfd, &remoteAdr, &ssize)) == int(INVALID_SOCKET)){ - handle_errno(error, "SWBaseSocket::accept() error: "); - return NULL; - } - - //nonblocking? - if( block_mode == nonblocking ) - fcntl(remotefd, F_SETFL, O_NONBLOCK); - - /* Create new class*/ - SWBaseSocket* remoteClass = create(remotefd, error); - if( remoteClass == NULL ) - return NULL; - - no_error(error); - return remoteClass; -} - -bool SWBaseSocket::disconnect(SWBaseError *error) -{ - int n = 0; - char buf[256]; - - if(myfd < 0){ - set_error(error, notConnected, "SWBaseSocket::disconnect() - No connection"); - return false; - } - - //close WR (this signals the peer) - if( shutdown(myfd, 1) != 0 ){ - handle_errno(error, "SWBaseSocket::disconnect() error: "); - return false; - } - - - SWBaseError err; - - //wait for close signal from peer - if( recv_close == false ){ - while(true){ - if( !waitRead(error) ) - return false; - - n = recv(buf, 256, &err); - - if( n <= 0 ) - break; - if(block_mode == noWait){ - //we don't want to block - set_error(error, notReady, "SWBaseSocket::disconnect() - Need more time, call again"); - return false; - } - } - } - - if( n != 0 ){ - set_error(error, err, error_string); - return false; //error - } - - //reset state - reset(); - - close(myfd); - myfd = -1; - - no_error(error); - return true; -} - -bool SWBaseSocket::close_fd() -{ - if( myfd > 0 ){ - close(myfd); - myfd = -1; - - //reset state - reset(); - - return true; - } - return false; -} - -int SWBaseSocket::send(const char *buf, int bytes, SWBaseError *error) -{ - int ret; - - if(myfd < 0){ - set_error(error, notConnected, "SWBaseSocket::send() - No connection"); - return -1; - } - - if( !waitWrite(error) ) - return -1; - - ret = ::send(myfd, buf, bytes, MSG_NOSIGNAL); - - if( ret < 0 ) - handle_errno(error, "SWBaseSocket::send() error: "); - else - no_error(error); - - return ret; -} - -int SWBaseSocket::fsend(const char *buf, int bytes, SWBaseError *error) -{ - int n; - int bytessent; - - if(fsend_ready){ - //First call - fsend_bytesleft = bytes; - fsend_total = fsend_bytesleft; //global var needed for resume - bytessent = 0; - fsend_ready = false; //point of no return - } - else{ - //resume - bytessent = fsend_total - fsend_bytesleft; - } - - //send package - while( fsend_bytesleft > 0 ){ - n = send( buf + bytessent , fsend_bytesleft, error ); - - //return on error, wouldblock or nowait - if( n < 0 ) - return ( (bytessent > 0 )? -bytessent : -1 ); - - bytessent += n; - fsend_bytesleft -= n; - - if ( block_mode == noWait && fsend_bytesleft > 0 ){ - set_error(error, notReady, "SWBaseSocket::fsend() - Need more time, call again"); - return -bytessent; - } - } - - fsend_ready = true; - - no_error(error); - return fsend_total; -} - -int SWBaseSocket::sendmsg(const string msg, SWBaseError *error) -{ - return send(msg.c_str(), msg.size(), error); -} - -int SWBaseSocket::fsendmsg(const string msg, SWBaseError *error) -{ - return fsend(msg.c_str(), msg.size(), error); -} - -int SWBaseSocket::recv(char *buf, int bytes, SWBaseError *error) -{ - int ret; - - if(myfd < 0){ - set_error(error, notConnected, "SWBaseSocket::recv() - No connection"); - return -1; - } - - if( !waitRead(error) ) - return -1; - - ret = ::recv(myfd, buf, bytes, MSG_NOSIGNAL); - - if( ret < 0 ) - handle_errno(error, "SWBaseSocket::recv() error: "); - else if( ret == 0 ){ - recv_close = true; //we recived a close signal from peer - set_error(error, terminated, "SWBaseSocket::recv() - Connection terminated by peer"); - }else - no_error(error); - - return ret; -} - -int SWBaseSocket::frecv(char *buf, int bytes, SWBaseError *error) -{ - int n; - int bytesrecv; - - if(frecv_ready){ - //First call - frecv_bytesleft = bytes; - frecv_total = frecv_bytesleft; //global var needed for resume - bytesrecv = 0; - frecv_ready = false; //point of no return - } - else{ - //resume - bytesrecv = frecv_total - frecv_bytesleft; - } - - - //recv package - while( frecv_bytesleft > 0 ){ - n = recv( buf + bytesrecv , frecv_bytesleft, error ); - - //return on error, wouldblock, nowait or timeout - if( n < 0 ) - return ( (bytesrecv > 0 )? -bytesrecv : -1 ); - if( n == 0 ) - return 0; // terminated - - bytesrecv += n; - frecv_bytesleft -= n; - - if ( block_mode == noWait && frecv_bytesleft > 0 ){ - set_error(error, notReady, "SWBaseSocket::frecv() - Need more time, call again"); - return -bytesrecv; - } - } - - frecv_ready = true; - - no_error(error); - return frecv_total; -} - -string SWBaseSocket::recvmsg(int bytes, SWBaseError *error) -{ - char *buf = new char[bytes+1]; - - SWBaseError err; - string msg = ""; - int ret = recv(buf, bytes, &err); - - if( ret > 0 ){ - buf[ret]='\0'; // Make sure the string is null terminated - msg = buf; - no_error(error); - } - delete[] buf; - - if( ret < 1 ) - set_error(error, err, err.get_error()); - - return msg; -} - -int SWBaseSocket::get_fd(SWBaseError *error) -{ - if( myfd > 0 ){ - no_error(error); - return myfd; - } - - set_error(error, notConnected, "SWBaseSocket::get_fd() - No descriptor"); - return -1; -} - -bool SWBaseSocket::get_host(sockaddr *host, SWBaseError *error) -{ - if( host == NULL){ - set_error(error, fatal, "SWBaseSocket::get_host() - Got NULL pointer"); - return false; - } - - if(myfd < 0){ - set_error(error, notConnected, "SWBaseSocket::get_host() - No socket"); - return false; - } - - sw_socklen_t tmp = sizeof(sockaddr); - if( getsockname(myfd, host, &tmp) != 0 ){ - handle_errno(error, "SWBaseSocket::get_host() error: "); - return false; - } - - no_error(error); - return true; -} - -bool SWBaseSocket::get_peer(sockaddr *peer, SWBaseError *error) -{ - if( peer == NULL){ - set_error(error, fatal, "SWBaseSocket::get_peer() - Got NULL pointer"); - return false; - } - - if(myfd > 0){ - sw_socklen_t tmp = sizeof(sockaddr); - if( getpeername(myfd, peer, &tmp) != 0 ){ - handle_errno(error, "SWBaseSocket::get_peer() error: "); - return false; - } - }else{ - set_error(error, notConnected, "SWBaseSocket::get_peer() - No connection"); - return false; - } - - no_error(error); - return true; -} - -void SWBaseSocket::reset() -{ - // Reset flags - recv_close = false; - - fsend_ready = true; - frecv_ready = true; -} - -bool SWBaseSocket::waitIO(io_type &type, SWBaseError *error) -{ - if( block_mode != blocking ){ - no_error(error); - return true; - } - - // We prefere to wait with select() even if no timeout is set - // as select() behaves more predictable - - timeval t; - timeval *to = NULL; // Indicate "wait for ever" - t.tv_sec = tsec; - t.tv_usec = tusec; - - if( tsec > 0 || tusec > 0 ) - to = &t; - - fd_set readfds, writefds, exceptfds; - - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&exceptfds); - FD_SET(myfd, &readfds); - FD_SET(myfd, &writefds); - FD_SET(myfd, &exceptfds); - - int ret = 0; - - switch (type){ - case read: - ret = select(myfd+1, &readfds, NULL, NULL, to); - break; - case write: - ret = select(myfd+1, NULL, &writefds, NULL, to); - break; - case except: - ret = select(myfd+1, NULL, NULL, &exceptfds, to); - break; - case rw: - ret = select(myfd+1, &readfds, &writefds, NULL, to); - break; - case all: - ret = select(myfd+1, &readfds, &writefds, &exceptfds, to); - break; - } - - if( ret < 0 ){ - handle_errno(error, "SWBaseSocket::waitIO() error: "); - return false; - } - if( ret == 0 ){ - set_error(error, timeout, "SWBaseSocket::waitIO() timeout"); - return false; - } - - if( FD_ISSET(myfd, &readfds) ){ - no_error(error); - type = read; - return true; - } - if( FD_ISSET(myfd, &writefds) ){ - no_error(error); - type = write; - return true; - } - if( FD_ISSET(myfd, &exceptfds) ){ - no_error(error); - type = except; - return true; - } - - set_error(error, fatal, "SWBaseSocket::waitIO() failed on select()"); - return false; -} - -bool SWBaseSocket::waitRead(SWBaseError *error) -{ - io_type tmp = read; - return waitIO(tmp, error); -} - -bool SWBaseSocket::waitWrite(SWBaseError *error) -{ - io_type tmp = write; - return waitIO(tmp, error); -} - -void SWBaseSocket::print_error() -{ - if( error_string.size() > 0 ) - fprintf(stderr, "%s!\n", error_string.c_str()); -} - -void SWBaseSocket::handle_errno(SWBaseError *error, string msg) -{ - #ifndef __WIN32__ - msg += strerror(errno); - #else - //stupid stupid stupid stupid M$ - switch (WSAGetLastError()){ - case 0: msg += "No error"; break; - case WSAEINTR: msg += "Interrupted system call"; break; - case WSAEBADF: msg += "Bad file number"; break; - case WSAEACCES: msg += "Permission denied"; break; - case WSAEFAULT: msg += "Bad address"; break; - case WSAEINVAL: msg += "Invalid argument"; break; - case WSAEMFILE: msg += "Too many open sockets"; break; - case WSAEWOULDBLOCK: msg += "Operation would block"; break; - case WSAEINPROGRESS: msg += "Operation now in progress"; break; - case WSAEALREADY: msg += "Operation already in progress"; break; - case WSAENOTSOCK: msg += "Socket operation on non-socket"; break; - case WSAEDESTADDRREQ: msg += "Destination address required"; break; - case WSAEMSGSIZE: msg += "Message too long"; break; - case WSAEPROTOTYPE: msg += "Protocol wrong type for socket"; break; - case WSAENOPROTOOPT: msg += "Bad protocol option"; break; - case WSAEPROTONOSUPPORT: msg += "Protocol not supported"; break; - case WSAESOCKTNOSUPPORT: msg += "Socket type not supported"; break; - case WSAEOPNOTSUPP: msg += "Operation not supported on socket"; break; - case WSAEPFNOSUPPORT: msg += "Protocol family not supported"; break; - case WSAEAFNOSUPPORT: msg += "Address family not supported"; break; - case WSAEADDRINUSE: msg += "Address already in use"; break; - case WSAEADDRNOTAVAIL: msg += "Can't assign requested address"; break; - case WSAENETDOWN: msg += "Network is down"; break; - case WSAENETUNREACH: msg += "Network is unreachable"; break; - case WSAENETRESET: msg += "Net connection reset"; break; - case WSAECONNABORTED: msg += "Software caused connection abort"; break; - case WSAECONNRESET: msg += "Connection reset by peer"; break; - case WSAENOBUFS: msg += "No buffer space available"; break; - case WSAEISCONN: msg += "Socket is already connected"; break; - case WSAENOTCONN: msg += "Socket is not connected"; break; - case WSAESHUTDOWN: msg += "Can't send after socket shutdown"; break; - case WSAETOOMANYREFS: msg += "Too many references"; break; - case WSAETIMEDOUT: msg += "Connection timed out"; break; - case WSAECONNREFUSED: msg += "Connection refused"; break; - case WSAELOOP: msg += "Too many levels of symbolic links"; break; - case WSAENAMETOOLONG: msg += "File name too long"; break; - case WSAEHOSTDOWN: msg += "Host is down"; break; - case WSAEHOSTUNREACH: msg += "No route to host"; break; - case WSAENOTEMPTY: msg += "Directory not empty"; break; - case WSAEPROCLIM: msg += "Too many processes"; break; - case WSAEUSERS: msg += "Too many users"; break; - case WSAEDQUOT: msg += "Disc quota exceeded"; break; - case WSAESTALE: msg += "Stale NFS file handle"; break; - case WSAEREMOTE: msg += "Too many levels of remote in path"; break; - case WSASYSNOTREADY: msg += "Network system is unavailable"; break; - case WSAVERNOTSUPPORTED: msg += "Winsock version out of range"; break; - case WSANOTINITIALISED: msg += "WSAStartup not yet called"; break; - case WSAEDISCON: msg += "Graceful shutdown in progress"; break; - case WSAHOST_NOT_FOUND: msg += "Host not found"; break; - case WSANO_DATA: msg += "No host data of that type was found"; break; - default: msg += "Unknown Winsock error: " + WSAGetLastError(); break; - } - #endif - - int errorno; - - //Win32 braindamage - #ifdef __WIN32__ - errorno = WSAGetLastError(); - #else - errorno = errno; - #endif - - SWBaseError e; - - if( errorno == EADDRINUSE ) - e = portInUse; - else if( errorno == EAGAIN || errorno == EWOULDBLOCK ) - e = notReady; - else if( errorno == EMSGSIZE ) - e = msgTooLong; - else if( errorno == EINPROGRESS || errorno == EALREADY ) - e = notReady; - else if( errorno == ECONNREFUSED || errorno == ETIMEDOUT ) - e = noResponse; - else if( errorno == ENOTCONN || errorno == EBADF || errorno == ENOTSOCK ) - e = notConnected; - else if( errorno == EPIPE ){ - e = terminated; - recv_close = true; - }else if( errorno == EINTR ) - e = interrupted; - else - e = fatal; //default - - set_error(error, e, msg); -} - -void SWBaseSocket::no_error(SWBaseError *error) -{ - if(error != NULL){ - *error = ok; - error->error_string = ""; - error->failed_class = NULL; - } -} - -void SWBaseSocket::set_error(SWBaseError *error, SWBaseError name, string msg) -{ - error_string = msg; - - if(error != NULL){ - *error = name; - error->error_string = msg; - error->failed_class = this; - }else{ - if( sw_Verbose ) - print_error(); - - if( sw_DoThrow ){ - SWBaseError e; - e = name; - e.error_string = msg; - e.failed_class = this; - throw e; - } - } -} - diff --git a/sockets/sw_base.h b/sockets/sw_base.h deleted file mode 100644 index 2c4e33ef..00000000 --- a/sockets/sw_base.h +++ /dev/null @@ -1,205 +0,0 @@ -// C++ Socket Wrapper -// SocketW base socket header -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindström - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#ifndef sw_base_H -#define sw_base_H - -#include "sw_internal.h" - -#include -#include - -// Set error handling mode -// throw_errors == true : Throws the error class on unhandled errors -// throw_errors == false : Exit on unhandled errors -// verbose == true : Prints the error message to stderr on unhandled errors -// -// Default is throw_errors == false and verbose == true -void sw_setThrowMode(bool throw_errors); -void sw_setVerboseMode(bool verbose); -bool sw_getThrowMode(void); -bool sw_getVerboseMode(void); - - -// Abstract base class for streaming sockets -class DECLSPEC SWBaseSocket -{ -public: - SWBaseSocket(); - virtual ~SWBaseSocket(); - - // Error types - // ok - operation succesful - // fatal - unspecified error - // notReady - you should call the function again - // indicates that the function would block (if nowait/nonblocking) - // portInUse - this port is used by another socket ( on listen() ) - // notConnected - socket not connected (or valid) - // msgTooLong - the message size it too big for send() - // terminated - connection terminated (by peer) - // noResponse - can't connect() to peer - // timeout - a read/write operation timed out (only if a timeout value is set and if in blocking mode) - // interrupted - operation was interrupted by a nonblocked signal - enum base_error{ok, fatal, notReady, portInUse, notConnected, msgTooLong, terminated, noResponse, timeout, interrupted}; - - class DECLSPEC SWBaseError - { - public: - SWBaseError(); - SWBaseError(base_error e); - - virtual ~SWBaseError(){;} - - virtual std::string get_error(); - virtual SWBaseSocket* get_failedClass(void); - - virtual bool operator==(SWBaseError e); - virtual bool operator!=(SWBaseError e); - - virtual void set_errorString(std::string msg); - virtual void set_failedClass(SWBaseSocket *pnt); - protected: - friend class SWBaseSocket; - - // The base error type - base_error be; - - // Human readable error string - std::string error_string; - - // A pointer to the class causing the error - SWBaseSocket *failed_class; - }; - - - // Note: If no SWBaseError class is provided with a method call (==NULL), - // SocketW will print the error to stderr and exit or throw on errors. - - // Note: All bool functions returns true on success. - - // Block mode - // blocking - everythings blocks until completly done - // noWait - operations block but only once - // useful with blocking w. select() - // nonblocking - don't block (you should use select()) - enum block_type{nonblocking, noWait, blocking}; - - - // Connection methods - // qLimit - the maximum length the queue of pending connections. - // Accept returns a new socket class connected with peer (should be - // freed with delete) or NULL on failure. You can cast the class to - // the correct type if you need to ( eg. (SWInetSocket *)mysocket ). - virtual bool listen(int qLimit = 5, SWBaseError *error = NULL); - virtual SWBaseSocket* accept(SWBaseError *error = NULL); - // bind() and connect() are implemented in child classes - - // do the disconnect ritual (signal peer, wait for close singal and close socket) - virtual bool disconnect(SWBaseError *error = NULL); - - // force close socket - virtual bool close_fd(); //use with care, disconnect() is cleaner - - // Direct I/O (raw) - // Can send/recv less bytes than specified! - // Returns the actual amount of bytes sent/recv on sucess - // and an negative integer on failure. - virtual int send(const char *buf, int bytes, SWBaseError *error = NULL); - virtual int sendmsg(const std::string msg, SWBaseError *error = NULL); - virtual int recv(char *buf, int bytes, SWBaseError *error = NULL); - virtual std::string recvmsg(int bytes = 256, SWBaseError *error = NULL); - - // Forced I/O - // Force system to send/recv the specified amount of bytes. - // On nowait/nonblocking: might return with notReady and then you - // MUST call the same method again (eg. wait with select() to know when) - // with the same parameters until the operation is finished. - // Returns 'bytes' when finished, negative integer on failure and - // 'notReady'. In the 'notReady' case, -(return value) is the amount of - // bytes sent/recv so far. - virtual int fsend(const char *buf, int bytes, SWBaseError *error = NULL); - virtual int fsendmsg(const std::string msg, SWBaseError *error = NULL); - virtual int frecv(char *buf, int bytes, SWBaseError *error = NULL); - - // Tools - // get_fd() - get socket descriptor, can be used with select() - // returns -1 on failure. - // get_host/peer fills the provided structures with info about the - // host/peer (see man unix & ip). - // SWInetSocket has some more tools for TCP/IP sockets. - virtual int get_fd(SWBaseError *error); - virtual bool get_host(sockaddr *host, SWBaseError *error = NULL); - virtual bool get_peer(sockaddr *peer, SWBaseError *error = NULL); - - // Set recv timeout (only in blocking mode). - // set_timeout(0,0) means wait forever (default). - // This affects the functions recv(), send(), accept() and disconnect() - // and others that use those, i.e. all frecvmsg(). - void set_timeout(Uint32 sec, Uint32 usec){ tsec = sec, tusec = usec; } - - // Error handling - virtual void print_error(); //prints the last error if any to stderr - virtual std::string get_error(){return error_string;} //returns a human readable error string - -protected: - // get a new socket if myfd < 0 - virtual void get_socket()=0; - - // create a new class for accept() using socketdescriptor - virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error)=0; - - // reset state - virtual void reset(); - - // wait for I/O (with timeout) - enum io_type{read, write, except, rw, all}; - virtual bool waitIO(io_type &type, SWBaseError *error); - bool waitRead(SWBaseError *error); - bool waitWrite(SWBaseError *error); - - // internal error handling - virtual void handle_errno(SWBaseError *error, std::string msg); - virtual void no_error(SWBaseError *error); - virtual void set_error(SWBaseError *error, SWBaseError name, std::string msg); - - // our socket descriptor - int myfd; - - // last error - std::string error_string; - - // data for fsend - bool fsend_ready; - int fsend_total; - int fsend_bytesleft; - - // data for frecv - bool frecv_ready; - int frecv_total; - int frecv_bytesleft; - - // have we recived a shutdown signal? - bool recv_close; - - //blocking mode (set by child classes) - block_type block_mode; - - //timeout for waitIO() - int tsec; - int tusec; -}; - - -#endif /* sw_base_H */ diff --git a/sockets/sw_inet.cpp b/sockets/sw_inet.cpp deleted file mode 100644 index c056722e..00000000 --- a/sockets/sw_inet.cpp +++ /dev/null @@ -1,249 +0,0 @@ -// C++ Socket Wrapper -// SocketW Inet socket -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindström - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#include "sw_inet.h" - -#ifndef __WIN32__ - #include - #include - #include - #include - #include -#else - #define F_SETFL FIONBIO - #define O_NONBLOCK 1 - - //Defined in sw_base.cxx - extern int close(int fd); - extern int fcntl(int fd, int cmd, long arg); -#endif - -using namespace std; - -//==================================================================== -//== SWInetSocket -//== Inet (TCP/IP) streaming sockets -//==================================================================== -SWInetSocket::SWInetSocket(block_type block) -{ - block_mode = block; -} - -SWInetSocket::~SWInetSocket() -{ - -} - -void SWInetSocket::get_socket() -{ - if( myfd < 0 ){ - myfd = socket(PF_INET, SOCK_STREAM, 0); - - if( block_mode == nonblocking ) - fcntl(myfd, F_SETFL, O_NONBLOCK); - - //reset state - reset(); - } -} - -SWBaseSocket* SWInetSocket::create(int socketdescriptor, SWBaseError *error) -{ - SWInetSocket* remoteClass; - - /* Create new class*/ - remoteClass = new SWInetSocket(block_mode); - remoteClass->myfd = socketdescriptor; - - no_error(error); - return remoteClass; -} - -bool SWInetSocket::bind(int port, SWBaseError *error) -{ - return bind(port, "", error); -} - -bool SWInetSocket::bind(int port, string host, SWBaseError *error) -{ - hostent *h; - in_addr inp; - - if( host.size() > 0 ){ - // Bind to a specific address - - if( (h = gethostbyname(host.c_str())) == NULL ){ - set_error(error, fatal, "SWInetSocket::bind() - Can't get host by name"); - return false; - } - - inp = *((in_addr *)h->h_addr); - }else{ - // Bind to any - inp.s_addr = INADDR_ANY; - } - - - get_socket(); - - sockaddr_in myAdr; - - memset(&myAdr, 0, sizeof(myAdr)); - myAdr.sin_family = AF_INET; - myAdr.sin_port = htons(port); - myAdr.sin_addr.s_addr = inp.s_addr; - - if(::bind(myfd, (sockaddr *)&myAdr, sizeof(myAdr)) == -1){ - handle_errno(error, "SWInetSocket::bind() error: "); - return false; - } - - no_error(error); - return true; -} - -bool SWInetSocket::connect(int port, string hostname, SWBaseError *error) -{ - get_socket(); - - hostent *host; - - if( (host = gethostbyname(hostname.c_str())) == NULL ){ - set_error(error, fatal, "SWInetSocket::connect() - Can't get host by name"); - return false; - } - - sockaddr_in remoteAdr; - - memset(&remoteAdr, 0, sizeof(remoteAdr)); - remoteAdr.sin_family = AF_INET; - remoteAdr.sin_port = htons(port); - remoteAdr.sin_addr = *((in_addr *)host->h_addr); - - if(::connect(myfd, (sockaddr *)&remoteAdr, sizeof(remoteAdr)) == -1){ - handle_errno(error, "SWInetSocket::connect() error: "); - return false; - } - - no_error(error); - return true; -} - - -string SWInetSocket::get_peerAddr(SWBaseError *error) -{ - sockaddr_in adr; - - if( !get_peer((sockaddr *)&adr, error) ) - return ""; - - char *pnt; - - if( (pnt = inet_ntoa(adr.sin_addr)) == NULL ){ - set_error(error, fatal, "SWInetSocket::get_peerName() - Can't get peer address"); - return ""; - } - string name(pnt); - - no_error(error); - return name; -} - -int SWInetSocket::get_peerPort(SWBaseError *error) -{ - sockaddr_in adr; - - if( !get_peer((sockaddr *)&adr, error) ) - return -1; - - no_error(error); - - return ntohs(adr.sin_port); -} - -string SWInetSocket::get_peerName(SWBaseError *error) -{ - string name = get_peerAddr(error); - if(name.size() < 1) - return ""; - - - hostent *h; - - if( (h = gethostbyname(name.c_str())) == NULL ){ - set_error(error, fatal, "SWInetSocket::get_peerName() - Can't get peer by address"); - return ""; - } - string host_name(h->h_name); - - no_error(error); - return host_name; -} - -string SWInetSocket::get_hostAddr(SWBaseError *error) -{ - //We need to get the real address, so we must - //first get this computers host name and then - //translate that into an address! - - string name = get_hostName(error); - if( name.size() < 1 ) - return ""; - - hostent *host; - - if( (host = gethostbyname(name.c_str())) == NULL ){ - set_error(error, fatal, "SWInetSocket::get_hostAddr() - Can't get host by name"); - return ""; - } - - char *pnt; - - if( (pnt = inet_ntoa(*((in_addr *)host->h_addr))) == NULL){ - set_error(error, fatal, "SWInetSocket::get_hostAddr() - Can't get host address"); - return ""; - } - - string adr(pnt); - - return adr; -} - -int SWInetSocket::get_hostPort(SWBaseError *error) -{ - sockaddr_in adr; - - if( !get_host((sockaddr *)&adr, error) ) - return -1; - - no_error(error); - - return ntohs(adr.sin_port); -} - -string SWInetSocket::get_hostName(SWBaseError *error) -{ - char buf[256]; - - if( gethostname(buf, 256) != 0 ){ - handle_errno(error, "SWInetSocket::gethostname() error: "); - return ""; - } - - string msg(buf); - - no_error(error); - return msg; -} diff --git a/sockets/sw_inet.h b/sockets/sw_inet.h deleted file mode 100644 index 26050845..00000000 --- a/sockets/sw_inet.h +++ /dev/null @@ -1,48 +0,0 @@ -// C++ Socket Wrapper -// SocketW Inet socket header -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindström - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#ifndef sw_inet_H -#define sw_inet_H - -#include "sw_internal.h" -#include "sw_base.h" -#include - -// Simple streaming TCP/IP class -class DECLSPEC SWInetSocket : public SWBaseSocket -{ -public: - SWInetSocket(block_type block=blocking); - virtual ~SWInetSocket(); - - virtual bool bind(int port, SWBaseError *error = NULL); //use port=0 to get any free port - virtual bool bind(int port, std::string host, SWBaseError *error = NULL); //you can also specify the host interface to use - virtual bool connect(int port, std::string hostname, SWBaseError *error = NULL); - - // Tools - // Gets IP addr, name or port. - virtual std::string get_peerAddr(SWBaseError *error = NULL); - virtual int get_peerPort(SWBaseError *error = NULL); - virtual std::string get_peerName(SWBaseError *error = NULL); - virtual std::string get_hostAddr(SWBaseError *error = NULL); - virtual int get_hostPort(SWBaseError *error = NULL); - virtual std::string get_hostName(SWBaseError *error = NULL); - -protected: - virtual void get_socket(); - virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error); -}; - -#endif /* sw_inet_H */ diff --git a/sockets/sw_internal.h b/sockets/sw_internal.h deleted file mode 100644 index 94313bcd..00000000 --- a/sockets/sw_internal.h +++ /dev/null @@ -1,75 +0,0 @@ -// C++ Socket Wrapper -// SocketW internal header -// -// Started 030823 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindstr�m - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#ifndef sw_internal_H -#define sw_internal_H - -// This header is included in all *.h files - -#ifndef __WIN32__ - #include - #include - #include - #include -#else - #include - - #define F_SETFL FIONBIO - #define O_NONBLOCK 1 -#endif - -#ifndef _SDL_H - -// Define general types -typedef unsigned char Uint8; -typedef signed char Sint8; -typedef unsigned short Uint16; -typedef signed short Sint16; -typedef unsigned int Uint32; -typedef signed int Sint32; - -// It's VERY important that these types really have the right sizes! -// This black magic is from SDL -#define COMPILE_TIME_ASSERT(name, x) \ - typedef int _dummy_ ## name[(x) * 2 - 1] -COMPILE_TIME_ASSERT(uint8, sizeof(Uint8) == 1); -COMPILE_TIME_ASSERT(sint8, sizeof(Sint8) == 1); -COMPILE_TIME_ASSERT(uint16, sizeof(Uint16) == 2); -COMPILE_TIME_ASSERT(sint16, sizeof(Sint16) == 2); -COMPILE_TIME_ASSERT(uint32, sizeof(Uint32) == 4); -COMPILE_TIME_ASSERT(sint32, sizeof(Sint32) == 4); -#undef COMPILE_TIME_ASSERT - -#endif /* _SDL_H */ - -// Some compilers use a special export keyword -#ifndef DECLSPEC - #ifdef __BEOS__ - #if defined(__GNUC__) - #define DECLSPEC __declspec(dllexport) - #else - #define DECLSPEC __declspec(export) - #endif - #else - #ifdef WIN32 - #define DECLSPEC __declspec(dllexport) - #else - #define DECLSPEC - #endif - #endif -#endif - - -#endif /* sw_internal_H */ diff --git a/sockets/sw_unix.cpp b/sockets/sw_unix.cpp deleted file mode 100644 index 07e2dd10..00000000 --- a/sockets/sw_unix.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// C++ Socket Wrapper -// SocketW Unix socket -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindström - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#include "sw_unix.h" -#include - -using namespace std; - -//==================================================================== -//== SWUnixSocket -//== Unix streaming sockets -//==================================================================== -#ifndef __WIN32__ - -SWUnixSocket::SWUnixSocket(block_type block) -{ - block_mode = block; -} - -SWUnixSocket::~SWUnixSocket() -{ - //nothing here -} - -void SWUnixSocket::get_socket() -{ - if( myfd < 0 ){ - myfd = socket(PF_UNIX, SOCK_STREAM, 0); - - if( block_mode == nonblocking ) - fcntl(myfd, F_SETFL, O_NONBLOCK); - - //reset state - reset(); - } -} - - -SWBaseSocket* SWUnixSocket::create(int socketdescriptor, SWBaseError *error) -{ - SWUnixSocket* remoteClass; - - /* Create new class*/ - remoteClass = new SWUnixSocket(block_mode); - remoteClass->myfd = socketdescriptor; - - no_error(error); - return remoteClass; -} - -bool SWUnixSocket::bind(string path, SWBaseError *error) -{ - get_socket(); - - sockaddr_un myAdr; - - myAdr.sun_family = AF_UNIX; - strncpy(myAdr.sun_path, path.c_str(), path.size()+1); - - if(::bind(myfd, (sockaddr *)&myAdr, sizeof(myAdr)) == -1){ - handle_errno(error, "SWUnixSocket::bind() error: "); - return false; - } - - no_error(error); - return true; -} - -bool SWUnixSocket::connect(string path, SWBaseError *error) -{ - get_socket(); - - sockaddr_un remoteAdr; - - remoteAdr.sun_family = AF_UNIX; - strncpy(remoteAdr.sun_path, path.c_str(), path.size()+1); - - if(::connect(myfd, (sockaddr *)&remoteAdr, sizeof(remoteAdr)) == -1){ - handle_errno(error, "SWUnixSocket::connect() error: "); - return false; - } - - no_error(error); - return true; -} - -#endif /* __WIN32__ */ diff --git a/sockets/sw_unix.h b/sockets/sw_unix.h deleted file mode 100644 index 576f14c0..00000000 --- a/sockets/sw_unix.h +++ /dev/null @@ -1,41 +0,0 @@ -// C++ Socket Wrapper -// SocketW Unix socket header -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindström - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#ifndef sw_unix_H -#define sw_unix_H -#ifndef __WIN32__ - -#include "sw_internal.h" -#include "sw_base.h" -#include - -// Simple streaming Unix class -class DECLSPEC SWUnixSocket : public SWBaseSocket -{ -public: - SWUnixSocket(block_type block=blocking); - ~SWUnixSocket(); - - // bind and connect to the socket file "path" - virtual bool bind(std::string path, SWBaseError *error = NULL); - virtual bool connect(std::string path, SWBaseError *error = NULL); - -protected: - virtual void get_socket(); - virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error); -}; - -#endif /* __WIN32__ */ -#endif /* sw_unix_H */ From f801beab75c25ceb408acceb7b183a63e23b269f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 11 Apr 2011 17:05:36 +0200 Subject: [PATCH 06/15] Even more documentation, RTMP Connector compiles again, but still extremely buggy. Will create RTMP debugging tool soon. Something wrong with RTMP Connector reading FLV::Tags... needs more investigation. --- Doxyfile | 2 +- util/amf.cpp | 3 + util/amf.h | 3 + util/crypto.cpp | 509 ++++++++++++++++++++++++++++++++++++++++++ util/crypto.h | 56 +++++ util/ddv_socket.cpp | 18 ++ util/ddv_socket.h | 4 + util/flv_tag.cpp | 22 +- util/flv_tag.h | 6 +- util/http_parser.cpp | 132 ++++++++--- util/http_parser.h | 74 +++--- util/rtmpchunks.cpp | 442 ++++++++++++++++++++++++++++++++++++ util/rtmpchunks.h | 65 ++++++ util/server_setup.cpp | 40 +++- 14 files changed, 1287 insertions(+), 89 deletions(-) create mode 100644 util/crypto.cpp create mode 100644 util/crypto.h create mode 100644 util/rtmpchunks.cpp create mode 100644 util/rtmpchunks.h diff --git a/Doxyfile b/Doxyfile index dd8ba205..b6edff54 100644 --- a/Doxyfile +++ b/Doxyfile @@ -43,7 +43,7 @@ SYMBOL_CACHE_SIZE = 0 # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES -EXTRACT_PRIVATE = NO +EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO diff --git a/util/amf.cpp b/util/amf.cpp index fdff660f..97d97ef5 100644 --- a/util/amf.cpp +++ b/util/amf.cpp @@ -1,3 +1,6 @@ +/// \file amf.cpp +/// Holds all code for the AMF namespace. + #include "amf.h" /// Returns the std::string Indice for the current object, if available. diff --git a/util/amf.h b/util/amf.h index 81ab9881..8ae95cd4 100644 --- a/util/amf.h +++ b/util/amf.h @@ -1,3 +1,6 @@ +/// \file amf.h +/// Holds all headers for the AMF namespace. + #pragma once #include #include diff --git a/util/crypto.cpp b/util/crypto.cpp new file mode 100644 index 00000000..c523c680 --- /dev/null +++ b/util/crypto.cpp @@ -0,0 +1,509 @@ +/// \file crypto.cpp +/// Holds all code needed for RTMP cryptography. + +#define STR(x) (((std::string)(x)).c_str()) + +#include "crypto.h" + +#define P768 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF" + +#define P1024 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \ +"FFFFFFFFFFFFFFFF" + +#define Q1024 \ +"7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \ +"948127044533E63A0105DF531D89CD9128A5043CC71A026E" \ +"F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \ +"F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \ +"F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \ +"FFFFFFFFFFFFFFFF" + +#define P1536 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF" + +#define P2048 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ +"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ +"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ +"15728E5A8AACAA68FFFFFFFFFFFFFFFF" + +#define P3072 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ +"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ +"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ +"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ +"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ +"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ +"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ +"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ +"43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF" + +#define P4096 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ +"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ +"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ +"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ +"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ +"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ +"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ +"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ +"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ +"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ +"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ +"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ +"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ +"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" \ +"FFFFFFFFFFFFFFFF" + +#define P6144 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ +"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ +"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ +"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ +"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ +"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ +"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ +"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ +"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ +"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ +"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ +"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ +"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ +"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ +"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ +"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ +"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ +"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ +"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ +"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ +"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ +"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ +"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ +"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ +"12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF" + +#define P8192 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ +"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ +"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ +"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ +"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ +"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ +"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ +"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ +"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ +"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ +"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ +"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ +"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ +"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ +"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ +"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ +"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ +"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ +"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ +"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ +"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ +"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ +"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ +"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ +"12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" \ +"38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" \ +"741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" \ +"3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" \ +"22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" \ +"4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" \ +"062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" \ +"4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" \ +"B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" \ +"4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" \ +"9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" \ +"60C980DD98EDD3DFFFFFFFFFFFFFFFFF" + + +uint8_t genuineFMSKey[] = { + 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, + 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, + 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, + 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001 + 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, + 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, + 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, + 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae +}; // 68 + +uint8_t genuineFPKey[] = { + 0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20, + 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C, + 0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79, + 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001 + 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, + 0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57, + 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, + 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE +}; // 62 + + +void replace(std::string &target, std::string search, std::string replacement) { + if (search == replacement) + return; + if (search == "") + return; + std::string::size_type i = std::string::npos; + while ((i = target.find(search)) != std::string::npos) { + target.replace(i, search.length(), replacement); + } +} + + +DHWrapper::DHWrapper(int32_t bitsCount) { + _bitsCount = bitsCount; + _pDH = NULL; + _pSharedKey = NULL; + _sharedKeyLength = 0; + _peerPublickey = NULL; +} + +DHWrapper::~DHWrapper() { + Cleanup(); +} + +bool DHWrapper::Initialize() { + Cleanup(); + + //1. Create the DH + _pDH = DH_new(); + if (_pDH == NULL) { + Cleanup(); + return false; + } + + //2. Create his internal p and g + _pDH->p = BN_new(); + if (_pDH->p == NULL) { + Cleanup(); + return false; + } + _pDH->g = BN_new(); + if (_pDH->g == NULL) { + Cleanup(); + return false; + } + + //3. initialize p, g and key length + if (BN_hex2bn(&_pDH->p, P1024) == 0) { + Cleanup(); + return false; + } + if (BN_set_word(_pDH->g, 2) != 1) { + Cleanup(); + return false; + } + + //4. Set the key length + _pDH->length = _bitsCount; + + //5. Generate private and public key + if (DH_generate_key(_pDH) != 1) { + Cleanup(); + return false; + } + + return true; +} + +bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength) { + if (_pDH == NULL) { + return false; + } + + return CopyKey(_pDH->pub_key, pDst, dstLength); +} + +bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength) { + if (_pDH == NULL) { + return false; + } + + return CopyKey(_pDH->priv_key, pDst, dstLength); +} + +bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length) { + if (_pDH == NULL) { + return false; + } + + if (_sharedKeyLength != 0 || _pSharedKey != NULL) { + return false; + } + + _sharedKeyLength = DH_size(_pDH); + if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024) { + return false; + } + _pSharedKey = new uint8_t[_sharedKeyLength]; + + _peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0); + if (_peerPublickey == NULL) { + return false; + } + + if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength) { + return false; + } + + return true; +} + +bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength) { + if (_pDH == NULL) { + return false; + } + + if (dstLength != _sharedKeyLength) { + return false; + } + + memcpy(pDst, _pSharedKey, _sharedKeyLength); + + return true; +} + +void DHWrapper::Cleanup() { + if (_pDH != NULL) { + if (_pDH->p != NULL) { + BN_free(_pDH->p); + _pDH->p = NULL; + } + if (_pDH->g != NULL) { + BN_free(_pDH->g); + _pDH->g = NULL; + } + DH_free(_pDH); + _pDH = NULL; + } + + if (_pSharedKey != NULL) { + delete[] _pSharedKey; + _pSharedKey = NULL; + } + _sharedKeyLength = 0; + + if (_peerPublickey != NULL) { + BN_free(_peerPublickey); + _peerPublickey = NULL; + } +} + +bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength) { + int32_t keySize = BN_num_bytes(pNum); + if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)) { + return false; + } + + if (BN_bn2bin(pNum, pDst) != keySize) { + return false; + } + + return true; +} + +void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut) { + uint8_t digest[SHA256_DIGEST_LENGTH]; + unsigned int digestLen = 0; + + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); + HMAC_Update(&ctx, pubKeyIn, 128); + HMAC_Final(&ctx, digest, &digestLen); + HMAC_CTX_cleanup(&ctx); + + RC4_set_key(rc4keyOut, 16, digest); + + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); + HMAC_Update(&ctx, pubKeyOut, 128); + HMAC_Final(&ctx, digest, &digestLen); + HMAC_CTX_cleanup(&ctx); + + RC4_set_key(rc4keyIn, 16, digest); +} + +std::string md5(std::string source, bool textResult) { + EVP_MD_CTX mdctx; + unsigned char md_value[EVP_MAX_MD_SIZE]; + unsigned int md_len; + + EVP_DigestInit(&mdctx, EVP_md5()); + EVP_DigestUpdate(&mdctx, STR(source), source.length()); + EVP_DigestFinal_ex(&mdctx, md_value, &md_len); + EVP_MD_CTX_cleanup(&mdctx); + + if (textResult) { + std::string result = ""; + char tmp[3]; + for (uint32_t i = 0; i < md_len; i++) { + sprintf(tmp, "%02x", md_value[i]); + result += tmp; + } + return result; + } else { + return std::string((char *) md_value, md_len); + } +} + +std::string b64(std::string source) { + return b64((uint8_t *) STR(source), source.size()); +} + +std::string b64(uint8_t *pBuffer, uint32_t length) { + BIO *bmem; + BIO *b64; + BUF_MEM *bptr; + + b64 = BIO_new(BIO_f_base64()); + bmem = BIO_new(BIO_s_mem()); + + b64 = BIO_push(b64, bmem); + BIO_write(b64, pBuffer, length); + std::string result = ""; + if (BIO_flush(b64) == 1) { + BIO_get_mem_ptr(b64, &bptr); + result = std::string(bptr->data, bptr->length); + } + + BIO_free_all(b64); + + + replace(result, "\n", ""); + replace(result, "\r", ""); + + return result; +} + +std::string unb64(std::string source) { + return unb64((uint8_t *)STR(source),source.length()); +} + +std::string unb64(uint8_t *pBuffer, uint32_t length){ + // create a memory buffer containing base64 encoded data + //BIO* bmem = BIO_new_mem_buf((void*) STR(source), source.length()); + BIO* bmem = BIO_new_mem_buf((void *)pBuffer, length); + + // push a Base64 filter so that reading from buffer decodes it + BIO *bioCmd = BIO_new(BIO_f_base64()); + // we don't want newlines + BIO_set_flags(bioCmd, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_push(bioCmd, bmem); + + char *pOut = new char[length]; + + int finalLen = BIO_read(bmem, (void*) pOut, length); + BIO_free_all(bmem); + std::string result(pOut, finalLen); + delete[] pOut; + return result; +} + +void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult) { + unsigned int digestLen; + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, (unsigned char*) pKey, keyLength, EVP_sha256(), NULL); + HMAC_Update(&ctx, (unsigned char *) pData, dataLength); + HMAC_Final(&ctx, (unsigned char *) pResult, &digestLen); + HMAC_CTX_cleanup(&ctx); +} + +uint32_t GetDigestOffset0(uint8_t *pBuffer) { + uint32_t offset = pBuffer[8] + pBuffer[9] + pBuffer[10] + pBuffer[11]; + return (offset % 728) + 12; +} +uint32_t GetDigestOffset1(uint8_t *pBuffer) { + uint32_t offset = pBuffer[772] + pBuffer[773] + pBuffer[774] + pBuffer[775]; + return (offset % 728) + 776; +} +uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme){ + if (scheme == 0){return GetDigestOffset0(pBuffer);}else{return GetDigestOffset1(pBuffer);} +} +uint32_t GetDHOffset0(uint8_t *pBuffer) { + uint32_t offset = pBuffer[1532] + pBuffer[1533] + pBuffer[1534] + pBuffer[1535]; + return (offset % 632) + 772; +} +uint32_t GetDHOffset1(uint8_t *pBuffer) { + uint32_t offset = pBuffer[768] + pBuffer[769] + pBuffer[770] + pBuffer[771]; + return (offset % 632) + 8; +} +uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme){ + if (scheme == 0){return GetDHOffset0(pBuffer);}else{return GetDHOffset1(pBuffer);} +} + + +bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme) { + uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme); + uint8_t *pTempBuffer = new uint8_t[1536 - 32]; + memcpy(pTempBuffer, pBuffer, clientDigestOffset); + memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32); + uint8_t *pTempHash = new uint8_t[512]; + HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash); + bool result = (memcmp(pBuffer+clientDigestOffset, pTempHash, 32) == 0); + #if DEBUG >= 4 + fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed"); + #endif + delete[] pTempBuffer; + delete[] pTempHash; + return result; +} diff --git a/util/crypto.h b/util/crypto.h new file mode 100644 index 00000000..f4daa4bb --- /dev/null +++ b/util/crypto.h @@ -0,0 +1,56 @@ +/// \file crypto.h +/// Holds all headers needed for RTMP cryptography functions. + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class DHWrapper { +private: + int32_t _bitsCount; + DH *_pDH; + uint8_t *_pSharedKey; + int32_t _sharedKeyLength; + BIGNUM *_peerPublickey; +public: + DHWrapper(int32_t bitsCount); + virtual ~DHWrapper(); + + bool Initialize(); + bool CopyPublicKey(uint8_t *pDst, int32_t dstLength); + bool CopyPrivateKey(uint8_t *pDst, int32_t dstLength); + bool CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length); + bool CopySharedKey(uint8_t *pDst, int32_t dstLength); +private: + void Cleanup(); + bool CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength); +}; + + +void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut); +std::string md5(std::string source, bool textResult); +std::string b64(std::string source); +std::string b64(uint8_t *pBuffer, uint32_t length); +std::string unb64(std::string source); +std::string unb64(uint8_t *pBuffer, uint32_t length); + +void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult); + +uint32_t GetDigestOffset0(uint8_t *pBuffer); +uint32_t GetDigestOffset1(uint8_t *pBuffer); +uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme); +uint32_t GetDHOffset0(uint8_t *pBuffer); +uint32_t GetDHOffset1(uint8_t *pBuffer); +uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme); + +extern uint8_t genuineFMSKey[]; + +bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme); diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 76c2ee8a..e82ea8ae 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -1,3 +1,6 @@ +/// \file ddv_socket.cpp +/// Holds all code for the DDV namespace. + #include "ddv_socket.h" /// Create a new base socket. This is a basic constructor for converting any valid socket to a DDV::Socket. @@ -207,6 +210,21 @@ int DDV::Socket::iread(void * buffer, int len){ return r; }//DDV::Socket::iread +/// Read call that is compatible with std::string. +/// Data is read using iread (which is nonblocking if the DDV::Socket itself is), +/// then appended to end of buffer. +/// \param buffer std::string to append data to. +/// \return True if new data arrived, false otherwise. +bool DDV::Socket::read(std::string & buffer){ + char cbuffer[5000]; + int num = iread(cbuffer, 5000); + if (num > 0){ + buffer.append(cbuffer, num); + return true; + } + return false; +}//read + /// Create a new base ServerSocket. The socket is never connected, and a placeholder for later connections. DDV::ServerSocket::ServerSocket(){ sock = -1; diff --git a/util/ddv_socket.h b/util/ddv_socket.h index 298b5954..5d58e0bd 100644 --- a/util/ddv_socket.h +++ b/util/ddv_socket.h @@ -1,3 +1,6 @@ +/// \file ddv_socket.h +/// Holds all headers for the DDV namespace. + #pragma once #include #include @@ -33,6 +36,7 @@ namespace DDV{ bool write(const std::string data); ///< Write call that is compatible with std::string. int iwrite(void * buffer, int len); ///< Incremental write call. int iread(void * buffer, int len); ///< Incremental read call. + bool read(std::string & buffer); ///< Read call that is compatible with std::string. void close(); ///< Close connection. int getSocket(); ///< Returns internal socket number. }; diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 70321b17..08fed05b 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -1,3 +1,6 @@ +/// \file flv_tag.cpp +/// Holds all code for the FLV namespace. + #include "flv_tag.h" #include //for Tag::FileLoader #include //for Tag::FileLoader @@ -8,6 +11,7 @@ char FLV::Header[13]; ///< Holds the last FLV header parsed. bool FLV::Parse_Error = false; ///< This variable is set to true if a problem is encountered while parsing the FLV. +std::string FLV::Error_Str = ""; /// Checks a FLV Header for validness. Returns true if the header is valid, false /// if the header is not. Not valid can mean: @@ -219,7 +223,7 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ if (FLV::check_header(data)){ sofar = 0; memcpy(FLV::Header, data, 13); - }else{FLV::Parse_Error = true; return false;} + }else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} } }else{ //if a tag header, calculate length and read tag body @@ -227,7 +231,7 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ len += (data[2] << 8); len += (data[1] << 16); if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){FLV::Parse_Error = true; return false;} + if (data[0] > 0x12){FLV::Parse_Error = true; Error_Str = "Invalid Tag received."; return false;} done = false; } } @@ -259,7 +263,7 @@ bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & s if (r < 0){ if (errno != EWOULDBLOCK){ FLV::Parse_Error = true; - fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); + Error_Str = "Error reading from socket."; } return false; } @@ -267,7 +271,7 @@ bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & s if (sofar == count){return true;} if (sofar > count){ FLV::Parse_Error = true; - fprintf(stderr, "ReadUntil fail: %s. Read too much. All Hell Broke Loose!\n", strerror(errno)); + Error_Str = "Socket buffer overflow."; } return false; }//Tag::SockReadUntil @@ -289,7 +293,7 @@ bool FLV::Tag::SockLoader(DDV::Socket sock){ if (FLV::check_header(data)){ sofar = 0; memcpy(FLV::Header, data, 13); - }else{FLV::Parse_Error = true; return false;} + }else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} } }else{ //if a tag header, calculate length and read tag body @@ -297,7 +301,7 @@ bool FLV::Tag::SockLoader(DDV::Socket sock){ len += (data[2] << 8); len += (data[1] << 16); if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){FLV::Parse_Error = true; return false;} + if (data[0] > 0x12){FLV::Parse_Error = true; Error_Str = "Invalid Tag received."; return false;} done = false; } } @@ -335,7 +339,7 @@ bool FLV::Tag::FileReadUntil(char * buffer, unsigned int count, unsigned int & s if (sofar >= count){return true;} int r = 0; r = fread(buffer + sofar,1,count-sofar,f); - if (r < 0){FLV::Parse_Error = true; return false;} + if (r < 0){FLV::Parse_Error = true; Error_Str = "File reading error."; return false;} sofar += r; if (sofar >= count){return true;} return false; @@ -363,7 +367,7 @@ bool FLV::Tag::FileLoader(FILE * f){ if (FLV::check_header(data)){ sofar = 0; memcpy(FLV::Header, data, 13); - }else{FLV::Parse_Error = true;} + }else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} } }else{ //if a tag header, calculate length and read tag body @@ -371,7 +375,7 @@ bool FLV::Tag::FileLoader(FILE * f){ len += (data[2] << 8); len += (data[1] << 16); if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){FLV::Parse_Error = true;} + if (data[0] > 0x12){FLV::Parse_Error = true; Error_Str = "Invalid Tag received."; return false;} done = false; } } diff --git a/util/flv_tag.h b/util/flv_tag.h index d91cabb5..e824228c 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -1,3 +1,6 @@ +/// \file flv_tag.h +/// Holds all headers for the FLV namespace. + #pragma once #include "ddv_socket.h" #include @@ -7,7 +10,8 @@ namespace FLV { //variables extern char Header[13]; ///< Holds the last FLV header parsed. extern bool Parse_Error; ///< This variable is set to true if a problem is encountered while parsing the FLV. - + extern std::string Error_Str; ///< This variable is set if a problem is encountered while parsing the FLV. + //functions bool check_header(char * header); ///< Checks a FLV Header for validness. bool is_header(char * header); ///< Checks the first 3 bytes for the string "FLV". diff --git a/util/http_parser.cpp b/util/http_parser.cpp index dd01e9b2..60d370e7 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -1,8 +1,14 @@ -#include "http_parser.h" -#include "ddv_socket.h" +/// \file http_parser.cpp +/// Holds all code for the HTTP namespace. -HTTPReader::HTTPReader(){Clean();} -void HTTPReader::Clean(){ +#include "http_parser.h" + +/// This constructor creates an empty HTTP::Parser, ready for use for either reading or writing. +/// All this constructor does is call HTTP::Parser::Clean(). +HTTP::Parser::Parser(){Clean();} + +/// Completely re-initializes the HTTP::Parser, leaving it ready for either reading or writing usage. +void HTTP::Parser::Clean(){ seenHeaders = false; seenReq = false; method = "GET"; @@ -11,11 +17,14 @@ void HTTPReader::Clean(){ body = ""; length = 0; HTTPbuffer = ""; - headers.erase(headers.begin(), headers.end()); - vars.erase(vars.begin(), vars.end()); + headers.clear(); + vars.clear(); } -bool HTTPReader::CleanForNext(){ +/// Re-initializes the HTTP::Parser, leaving the internal data buffer alone, then tries to parse a new request or response. +/// First does the same as HTTP::Parser::Clean(), but does not clear the internal data buffer. +/// This function then calls the HTTP::Parser::parse() function, and returns that functions return value. +bool HTTP::Parser::CleanForNext(){ seenHeaders = false; seenReq = false; method = "GET"; @@ -23,12 +32,19 @@ bool HTTPReader::CleanForNext(){ protocol = "HTTP/1.1"; body = ""; length = 0; - headers.erase(headers.begin(), headers.end()); - vars.erase(vars.begin(), vars.end()); + headers.clear(); + vars.clear(); return parse(); } -std::string HTTPReader::BuildRequest(){ +/// Returns a string containing a valid HTTP 1.0 or 1.1 request, ready for sending. +/// The request is build from internal variables set before this call is made. +/// To be precise, method, url, protocol, headers and the internal data buffer are used, +/// where the internal data buffer is used as the body of the request. +/// This means you cannot mix receiving and sending, because the body would get corrupted. +/// \return A string containing a valid HTTP 1.0 or 1.1 request, ready for sending. +std::string HTTP::Parser::BuildRequest(){ + /// \todo Include GET/POST variable parsing? std::map::iterator it; std::string tmp = method+" "+url+" "+protocol+"\n"; for (it=headers.begin(); it != headers.end(); it++){ @@ -39,7 +55,16 @@ std::string HTTPReader::BuildRequest(){ return tmp; } -std::string HTTPReader::BuildResponse(std::string code, std::string message){ +/// Returns a string containing a valid HTTP 1.0 or 1.1 response, ready for sending. +/// The response is partly build from internal variables set before this call is made. +/// To be precise, protocol, headers and the internal data buffer are used, +/// where the internal data buffer is used as the body of the response. +/// This means you cannot mix receiving and sending, because the body would get corrupted. +/// \param code The HTTP response code. Usually you want 200. +/// \param message The HTTP response message. Usually you want "OK". +/// \return A string containing a valid HTTP 1.0 or 1.1 response, ready for sending. +std::string HTTP::Parser::BuildResponse(std::string code, std::string message){ + /// \todo Include GET/POST variable parsing? std::map::iterator it; std::string tmp = protocol+" "+code+" "+message+"\n"; for (it=headers.begin(); it != headers.end(); it++){ @@ -50,47 +75,61 @@ std::string HTTPReader::BuildResponse(std::string code, std::string message){ return tmp; } -void HTTPReader::Trim(std::string & s){ +/// Trims any whitespace at the front or back of the string. +/// Used when getting/setting headers. +/// \param s The string to trim. The string itself will be changed, not returned. +void HTTP::Parser::Trim(std::string & s){ size_t startpos = s.find_first_not_of(" \t"); size_t endpos = s.find_last_not_of(" \t"); if ((std::string::npos == startpos) || (std::string::npos == endpos)){s = "";}else{s = s.substr(startpos, endpos-startpos+1);} } -void HTTPReader::SetBody(std::string s){ +/// Function that sets the body of a response or request, along with the correct Content-Length header. +/// \param s The string to set the body to. +void HTTP::Parser::SetBody(std::string s){ HTTPbuffer = s; SetHeader("Content-Length", s.length()); } -void HTTPReader::SetBody(char * buffer, int len){ +/// Function that sets the body of a response or request, along with the correct Content-Length header. +/// \param buffer The buffer data to set the body to. +/// \param len Length of the buffer data. +void HTTP::Parser::SetBody(char * buffer, int len){ HTTPbuffer = ""; HTTPbuffer.append(buffer, len); SetHeader("Content-Length", len); } +/// Returns header i, if set. +std::string HTTP::Parser::GetHeader(std::string i){return headers[i];} +/// Returns POST variable i, if set. +std::string HTTP::Parser::GetVar(std::string i){return vars[i];} -std::string HTTPReader::GetHeader(std::string i){return headers[i];} -std::string HTTPReader::GetVar(std::string i){return vars[i];} - -void HTTPReader::SetHeader(std::string i, std::string v){ +/// Sets header i to string value v. +void HTTP::Parser::SetHeader(std::string i, std::string v){ Trim(i); Trim(v); headers[i] = v; } -void HTTPReader::SetHeader(std::string i, int v){ +/// Sets header i to integer value v. +void HTTP::Parser::SetHeader(std::string i, int v){ Trim(i); char val[128]; sprintf(val, "%i", v); headers[i] = val; } -void HTTPReader::SetVar(std::string i, std::string v){ +/// Sets POST variable i to string value v. +void HTTP::Parser::SetVar(std::string i, std::string v){ Trim(i); Trim(v); vars[i] = v; } -bool HTTPReader::Read(DDV::Socket & sock){ +/// Attempt to read a whole HTTP request or response from DDV::Socket sock. +/// \return True of a whole request or response was read, false otherwise. +bool HTTP::Parser::Read(DDV::Socket & sock){ //returned true als hele http packet gelezen is int r = 0; int b = 0; @@ -111,7 +150,9 @@ bool HTTPReader::Read(DDV::Socket & sock){ return false; }//HTTPReader::ReadSocket -bool HTTPReader::Read(FILE * F){ +/// Reads a full set of HTTP responses/requests from file F. +/// \return Always false. Use HTTP::Parser::CleanForNext() to parse the contents of the file. +bool HTTP::Parser::Read(FILE * F){ //returned true als hele http packet gelezen is int b = 1; char buffer[500]; @@ -122,7 +163,11 @@ bool HTTPReader::Read(FILE * F){ return false; }//HTTPReader::ReadSocket -bool HTTPReader::parse(){ +/// Attempt to read a whole HTTP response or request from the internal data buffer. +/// If succesful, fills its own fields with the proper data and removes the response/request +/// from the internal data buffer. +/// \return True on success, false otherwise. +bool HTTP::Parser::parse(){ size_t f; std::string tmpA, tmpB, tmpC; while (HTTPbuffer != ""){ @@ -140,7 +185,7 @@ bool HTTPReader::parse(){ if (f != std::string::npos){url = tmpA.substr(0, f); tmpA.erase(0, f+1);} f = tmpA.find(' '); if (f != std::string::npos){protocol = tmpA.substr(0, f); tmpA.erase(0, f+1);} - //TODO: GET variable parsing? + /// \todo Include GET variable parsing? }else{ if (tmpA.size() == 0){ seenHeaders = true; @@ -156,7 +201,7 @@ bool HTTPReader::parse(){ } if (seenHeaders){ if (length > 0){ - //TODO: POST variable parsing? + /// \todo Include POST variable parsing? if (HTTPbuffer.length() >= length){ body = HTTPbuffer.substr(0, length); HTTPbuffer.erase(0, length); @@ -172,23 +217,40 @@ bool HTTPReader::parse(){ return false; //we should never get here... }//HTTPReader::parse -void HTTPReader::SendResponse(DDV::Socket & conn, std::string code, std::string message){ +/// Sends data as response to conn. +/// The response is automatically first build using HTTP::Parser::BuildResponse(). +/// \param conn The DDV::Socket to send the response over. +/// \param code The HTTP response code. Usually you want 200. +/// \param message The HTTP response message. Usually you want "OK". +void HTTP::Parser::SendResponse(DDV::Socket & conn, std::string code, std::string message){ std::string tmp = BuildResponse(code, message); conn.write(tmp); } -void HTTPReader::SendBodyPart(DDV::Socket & conn, char * buffer, int len){ +/// Sends data as HTTP/1.1 bodypart to conn. +/// HTTP/1.1 chunked encoding is automatically applied if needed. +/// \param conn The DDV::Socket to send the part over. +/// \param buffer The buffer to send. +/// \param len The length of the buffer. +void HTTP::Parser::SendBodyPart(DDV::Socket & conn, char * buffer, int len){ std::string tmp; tmp.append(buffer, len); SendBodyPart(conn, tmp); } -void HTTPReader::SendBodyPart(DDV::Socket & conn, std::string bodypart){ - static char len[10]; - int sizelen; - sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); - conn.write(len, sizelen); - conn.write(bodypart); - conn.write(len+sizelen-2, 2); +/// Sends data as HTTP/1.1 bodypart to conn. +/// HTTP/1.1 chunked encoding is automatically applied if needed. +/// \param conn The DDV::Socket to send the part over. +/// \param bodypart The data to send. +void HTTP::Parser::SendBodyPart(DDV::Socket & conn, std::string bodypart){ + if (protocol == "HTTP/1.1"){ + static char len[10]; + int sizelen; + sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); + conn.write(len, sizelen); + conn.write(bodypart); + conn.write(len+sizelen-2, 2); + }else{ + conn.write(bodypart); + } } - diff --git a/util/http_parser.h b/util/http_parser.h index 2741f0c4..d08cadef 100644 --- a/util/http_parser.h +++ b/util/http_parser.h @@ -1,3 +1,6 @@ +/// \file http_parser.h +/// Holds all headers for the HTTP namespace. + #pragma once #include #include @@ -5,37 +8,40 @@ #include #include "ddv_socket.h" -class HTTPReader{ - public: - HTTPReader(); - bool Read(DDV::Socket & sock); - bool Read(FILE * F); - std::string GetHeader(std::string i); - std::string GetVar(std::string i); - void SetHeader(std::string i, std::string v); - void SetHeader(std::string i, int v); - void SetVar(std::string i, std::string v); - void SetBody(std::string s); - void SetBody(char * buffer, int len); - std::string BuildRequest(); - std::string BuildResponse(std::string code, std::string message); - void SendResponse(DDV::Socket & conn, std::string code, std::string message); - void SendBodyPart(DDV::Socket & conn, char * buffer, int len); - void SendBodyPart(DDV::Socket & conn, std::string bodypart); - void Clean(); - bool CleanForNext(); - std::string body; - std::string method; - std::string url; - std::string protocol; - unsigned int length; - private: - bool seenHeaders; - bool seenReq; - bool parse(); - std::string HTTPbuffer; - std::map headers; - std::map vars; - void Trim(std::string & s); -};//HTTPReader - +/// Holds all HTTP processing related code. +namespace HTTP{ + /// Simple class for reading and writing HTTP 1.0 and 1.1. + class Parser{ + public: + Parser(); + bool Read(DDV::Socket & sock); + bool Read(FILE * F); + std::string GetHeader(std::string i); + std::string GetVar(std::string i); + void SetHeader(std::string i, std::string v); + void SetHeader(std::string i, int v); + void SetVar(std::string i, std::string v); + void SetBody(std::string s); + void SetBody(char * buffer, int len); + std::string BuildRequest(); + std::string BuildResponse(std::string code, std::string message); + void SendResponse(DDV::Socket & conn, std::string code, std::string message); + void SendBodyPart(DDV::Socket & conn, char * buffer, int len); + void SendBodyPart(DDV::Socket & conn, std::string bodypart); + void Clean(); + bool CleanForNext(); + std::string body; + std::string method; + std::string url; + std::string protocol; + unsigned int length; + private: + bool seenHeaders; + bool seenReq; + bool parse(); + std::string HTTPbuffer; + std::map headers; + std::map vars; + void Trim(std::string & s); + };//HTTP::Parser class +};//HTTP namespace diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp new file mode 100644 index 00000000..d297b5e5 --- /dev/null +++ b/util/rtmpchunks.cpp @@ -0,0 +1,442 @@ +/// \file rtmpchunks.cpp +/// Holds all code for the RTMPStream namespace. + +#include "rtmpchunks.h" +#include "crypto.h" + +char versionstring[] = "WWW.DDVTECH.COM "; ///< String that is repeated in the RTMP handshake +std::string RTMPStream::handshake_in; ///< Input for the handshake. +std::string RTMPStream::handshake_out;///< Output for the handshake. + +/// Gets the current system time in milliseconds. +unsigned int RTMPStream::getNowMS(){ + timeval t; + gettimeofday(&t, 0); + return t.tv_sec + t.tv_usec/1000; +}//RTMPStream::getNowMS + + +unsigned int RTMPStream::chunk_rec_max = 128; +unsigned int RTMPStream::chunk_snd_max = 128; +unsigned int RTMPStream::rec_window_size = 0xFA00; +unsigned int RTMPStream::snd_window_size = 1024*500; +unsigned int RTMPStream::rec_window_at = 0; +unsigned int RTMPStream::snd_window_at = 0; +unsigned int RTMPStream::rec_cnt = 0; +unsigned int RTMPStream::snd_cnt = 0; + +timeval RTMPStream::lastrec; +unsigned int RTMPStream::firsttime; + +/// Holds the last sent chunk for every msg_id. +std::map RTMPStream::Chunk::lastsend; +/// Holds the last received chunk for every msg_id. +std::map RTMPStream::Chunk::lastrecv; + +/// Packs up the chunk for sending over the network. +/// \warning Do not call if you are not actually sending the resulting data! +/// \returns A std::string ready to be sent. +std::string RTMPStream::Chunk::Pack(){ + std::string output = ""; + RTMPStream::Chunk prev = lastsend[cs_id]; + unsigned int tmpi; + unsigned char chtype = 0x00; + timestamp -= firsttime; + if (prev.cs_id == cs_id){ + if (msg_stream_id == prev.msg_stream_id){ + chtype = 0x40;//do not send msg_stream_id + if (len == prev.len){ + if (msg_type_id == prev.msg_type_id){ + chtype = 0x80;//do not send len and msg_type_id + if (timestamp == prev.timestamp){ + chtype = 0xC0;//do not send timestamp + } + } + } + } + } + if (cs_id <= 63){ + output += (unsigned char)(chtype | cs_id); + }else{ + if (cs_id <= 255+64){ + output += (unsigned char)(chtype | 0); + output += (unsigned char)(cs_id - 64); + }else{ + output += (unsigned char)(chtype | 1); + output += (unsigned char)((cs_id - 64) % 256); + output += (unsigned char)((cs_id - 64) / 256); + } + } + unsigned int ntime = 0; + if (chtype != 0xC0){ + //timestamp or timestamp diff + if (chtype == 0x00){ + tmpi = timestamp; + }else{ + tmpi = timestamp - prev.timestamp; + } + if (tmpi >= 0x00ffffff){ntime = tmpi; tmpi = 0x00ffffff;} + output += (unsigned char)(tmpi / (256*256)); + output += (unsigned char)(tmpi / 256); + output += (unsigned char)(tmpi % 256); + if (chtype != 0x80){ + //len + tmpi = len; + output += (unsigned char)(tmpi / (256*256)); + output += (unsigned char)(tmpi / 256); + output += (unsigned char)(tmpi % 256); + //msg type id + output += (unsigned char)msg_type_id; + if (chtype != 0x40){ + //msg stream id + output += (unsigned char)(msg_stream_id % 256); + output += (unsigned char)(msg_stream_id / 256); + output += (unsigned char)(msg_stream_id / (256*256)); + output += (unsigned char)(msg_stream_id / (256*256*256)); + } + } + } + //support for 0x00ffffff timestamps + if (ntime){ + output += (unsigned char)(ntime % 256); + output += (unsigned char)(ntime / 256); + output += (unsigned char)(ntime / (256*256)); + output += (unsigned char)(ntime / (256*256*256)); + } + len_left = 0; + while (len_left < len){ + tmpi = len - len_left; + if (tmpi > RTMPStream::chunk_snd_max){tmpi = RTMPStream::chunk_snd_max;} + output.append(data, len_left, tmpi); + len_left += tmpi; + if (len_left < len){ + if (cs_id <= 63){ + output += (unsigned char)(0xC0 + cs_id); + }else{ + if (cs_id <= 255+64){ + output += (unsigned char)(0xC0); + output += (unsigned char)(cs_id - 64); + }else{ + output += (unsigned char)(0xC1); + output += (unsigned char)((cs_id - 64) % 256); + output += (unsigned char)((cs_id - 64) / 256); + } + } + } + } + lastsend[cs_id] = *this; + RTMPStream::snd_cnt += output.size(); + return output; +}//SendChunk + +/// Default contructor, creates an empty chunk with all values initialized to zero. +RTMPStream::Chunk::Chunk(){ + cs_id = 0; + timestamp = 0; + len = 0; + real_len = 0; + len_left = 0; + msg_type_id = 0; + msg_stream_id = 0; + data = ""; +}//constructor + +/// Packs up a chunk with the given arguments as properties. +std::string RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data){ + RTMPStream::Chunk ch; + ch.cs_id = cs_id; + ch.timestamp = RTMPStream::getNowMS(); + ch.len = data.size(); + ch.real_len = data.size(); + ch.len_left = 0; + ch.msg_type_id = msg_type_id; + ch.msg_stream_id = msg_stream_id; + ch.data = data; + return ch.Pack(); +}//constructor + +/// Packs up a chunk with media contents. +/// \param msg_type_id Type number of the media, as per FLV standard. +/// \param data Contents of the media data. +/// \param len Length of the media data, in bytes. +/// \param ts Timestamp of the media data, relative to current system time. +std::string RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts){ + RTMPStream::Chunk ch; + ch.cs_id = msg_type_id; + ch.timestamp = ts; + ch.len = len; + ch.real_len = len; + ch.len_left = 0; + ch.msg_type_id = msg_type_id; + ch.msg_stream_id = 1; + ch.data.append((char*)data, (size_t)len); + return ch.Pack(); +}//SendMedia + +/// Packs up a chunk for a control message with 1 argument. +std::string RTMPStream::SendCTL(unsigned char type, unsigned int data){ + RTMPStream::Chunk ch; + ch.cs_id = 2; + ch.timestamp = RTMPStream::getNowMS(); + ch.len = 4; + ch.real_len = 4; + ch.len_left = 0; + ch.msg_type_id = type; + ch.msg_stream_id = 0; + ch.data.resize(4); + *(int*)((char*)ch.data.c_str()) = htonl(data); + return ch.Pack(); +}//SendCTL + +/// Packs up a chunk for a control message with 2 arguments. +std::string RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2){ + RTMPStream::Chunk ch; + ch.cs_id = 2; + ch.timestamp = RTMPStream::getNowMS(); + ch.len = 5; + ch.real_len = 5; + ch.len_left = 0; + ch.msg_type_id = type; + ch.msg_stream_id = 0; + ch.data.resize(5); + *(int*)((char*)ch.data.c_str()) = htonl(data); + ch.data[4] = data2; + return ch.Pack(); +}//SendCTL + +/// Packs up a chunk for a user control message with 1 argument. +std::string RTMPStream::SendUSR(unsigned char type, unsigned int data){ + RTMPStream::Chunk ch; + ch.cs_id = 2; + ch.timestamp = RTMPStream::getNowMS(); + ch.len = 6; + ch.real_len = 6; + ch.len_left = 0; + ch.msg_type_id = 4; + ch.msg_stream_id = 0; + ch.data.resize(6); + *(int*)((char*)ch.data.c_str()+2) = htonl(data); + ch.data[0] = 0; + ch.data[1] = type; + return ch.Pack(); +}//SendUSR + +/// Packs up a chunk for a user control message with 2 arguments. +std::string RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2){ + RTMPStream::Chunk ch; + ch.cs_id = 2; + ch.timestamp = RTMPStream::getNowMS(); + ch.len = 10; + ch.real_len = 10; + ch.len_left = 0; + ch.msg_type_id = 4; + ch.msg_stream_id = 0; + ch.data.resize(10); + *(int*)((char*)ch.data.c_str()+2) = htonl(data); + *(int*)((char*)ch.data.c_str()+6) = htonl(data2); + ch.data[0] = 0; + ch.data[1] = type; + return ch.Pack(); +}//SendUSR + + +/// Parses the argument string into the current chunk. +/// Tries to read a whole chunk, if successful it will remove +/// the corresponding data from the input string. +/// \param indata The input string to parse and update. +/// \warning This function will destroy the current data in this chunk! +/// \returns True if a whole chunk could be read, false otherwise. +bool RTMPStream::Chunk::Parse(std::string & indata){ + gettimeofday(&RTMPStream::lastrec, 0); + unsigned int i = 0; + if (indata.size() < 3) return false;//need at least 3 bytes to continue + + unsigned char chunktype = indata[i++]; + //read the chunkstream ID properly + switch (chunktype & 0x3F){ + case 0: + cs_id = indata[i++] + 64; + break; + case 1: + cs_id = indata[i++] + 64; + cs_id += indata[i++] * 256; + break; + default: + cs_id = chunktype & 0x3F; + break; + } + + RTMPStream::Chunk prev = lastrecv[cs_id]; + + //process the rest of the header, for each chunk type + switch (chunktype & 0xC0){ + case 0x00: + if (indata.size() < i+11) return false; //can't read whole header + timestamp = indata[i++]*256*256; + timestamp += indata[i++]*256; + timestamp += indata[i++]; + len = indata[i++]*256*256; + len += indata[i++]*256; + len += indata[i++]; + len_left = 0; + msg_type_id = indata[i++]; + msg_stream_id = indata[i++]; + msg_stream_id += indata[i++]*256; + msg_stream_id += indata[i++]*256*256; + msg_stream_id += indata[i++]*256*256*256; + break; + case 0x40: + if (indata.size() < i+7) return false; //can't read whole header + timestamp = indata[i++]*256*256; + timestamp += indata[i++]*256; + timestamp += indata[i++]; + timestamp += prev.timestamp; + len = indata[i++]*256*256; + len += indata[i++]*256; + len += indata[i++]; + len_left = 0; + msg_type_id = indata[i++]; + msg_stream_id = prev.msg_stream_id; + break; + case 0x80: + if (indata.size() < i+3) return false; //can't read whole header + timestamp = indata[i++]*256*256; + timestamp += indata[i++]*256; + timestamp += indata[i++]; + timestamp += prev.timestamp; + len = prev.len; + len_left = prev.len_left; + msg_type_id = prev.msg_type_id; + msg_stream_id = prev.msg_stream_id; + break; + case 0xC0: + timestamp = prev.timestamp; + len = prev.len; + len_left = prev.len_left; + msg_type_id = prev.msg_type_id; + msg_stream_id = prev.msg_stream_id; + break; + } + //calculate chunk length, real length, and length left till complete + if (len_left > 0){ + real_len = len_left; + len_left -= real_len; + }else{ + real_len = len; + } + if (real_len > RTMPStream::chunk_rec_max){ + len_left += real_len - RTMPStream::chunk_rec_max; + real_len = RTMPStream::chunk_rec_max; + } + //read extended timestamp, if neccesary + if (timestamp == 0x00ffffff){ + if (indata.size() < i+4) return false; //can't read whole header + timestamp = indata[i++]*256*256*256; + timestamp += indata[i++]*256*256; + timestamp += indata[i++]*256; + timestamp += indata[i++]; + } + + //read data if length > 0, and allocate it + if (real_len > 0){ + if (prev.len_left > 0){ + data = prev.data; + }else{ + data = ""; + } + if (indata.size() < i+real_len) return false;//can't read all data (yet) + data.append(indata, i, real_len); + indata = indata.substr(i+real_len); + lastrecv[cs_id] = *this; + RTMPStream::rec_cnt += i+real_len; + if (len_left == 0){ + return true; + }else{ + return false; + } + }else{ + data = ""; + indata = indata.substr(i+real_len); + lastrecv[cs_id] = *this; + RTMPStream::rec_cnt += i+real_len; + return true; + } +}//Parse + + +/// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. +/// After calling this function, don't forget to read and ignore 1536 extra bytes, +/// this is the handshake response and not interesting for us because we don't do client +/// verification. +bool RTMPStream::doHandshake(){ + char Version; + //Read C0 + Version = RTMPStream::handshake_in[0]; + uint8_t * Client = (uint8_t *)RTMPStream::handshake_in.c_str() + 1; + RTMPStream::handshake_out.resize(3073); + uint8_t * Server = (uint8_t *)RTMPStream::handshake_out.c_str() + 1; + RTMPStream::rec_cnt += 1537; + + //Build S1 Packet + *((uint32_t*)Server) = 0;//time zero + *(((uint32_t*)(Server+4))) = htonl(0x01020304);//version 1 2 3 4 + for (int i = 8; i < 3072; ++i){Server[i] = versionstring[i%16];}//"random" data + + bool encrypted = (Version == 6); + #if DEBUG >= 4 + fprintf(stderr, "Handshake version is %hhi\n", Version); + #endif + uint8_t _validationScheme = 5; + if (ValidateClientScheme(Client, 0)) _validationScheme = 0; + if (ValidateClientScheme(Client, 1)) _validationScheme = 1; + + #if DEBUG >= 4 + fprintf(stderr, "Handshake type is %hhi, encryption is %s\n", _validationScheme, encrypted?"on":"off"); + #endif + + //FIRST 1536 bytes from server response + //compute DH key position + uint32_t serverDHOffset = GetDHOffset(Server, _validationScheme); + uint32_t clientDHOffset = GetDHOffset(Client, _validationScheme); + + //generate DH key + DHWrapper dhWrapper(1024); + if (!dhWrapper.Initialize()) return false; + if (!dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false; + if (!dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false; + + if (encrypted) { + uint8_t secretKey[128]; + if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) return false; + RC4_KEY _pKeyIn; + RC4_KEY _pKeyOut; + InitRC4Encryption(secretKey, (uint8_t*) & Client[clientDHOffset], (uint8_t*) & Server[serverDHOffset], &_pKeyIn, &_pKeyOut); + uint8_t data[1536]; + RC4(&_pKeyIn, 1536, data, data); + RC4(&_pKeyOut, 1536, data, data); + } + //generate the digest + uint32_t serverDigestOffset = GetDigestOffset(Server, _validationScheme); + uint8_t *pTempBuffer = new uint8_t[1536 - 32]; + memcpy(pTempBuffer, Server, serverDigestOffset); + memcpy(pTempBuffer + serverDigestOffset, Server + serverDigestOffset + 32, 1536 - serverDigestOffset - 32); + uint8_t *pTempHash = new uint8_t[512]; + HMACsha256(pTempBuffer, 1536 - 32, genuineFMSKey, 36, pTempHash); + memcpy(Server + serverDigestOffset, pTempHash, 32); + delete[] pTempBuffer; + delete[] pTempHash; + + //SECOND 1536 bytes from server response + uint32_t keyChallengeIndex = GetDigestOffset(Client, _validationScheme); + pTempHash = new uint8_t[512]; + HMACsha256(Client + keyChallengeIndex, 32, genuineFMSKey, 68, pTempHash); + uint8_t *pLastHash = new uint8_t[512]; + HMACsha256(Server + 1536, 1536 - 32, pTempHash, 32, pLastHash); + memcpy(Server + 1536 * 2 - 32, pLastHash, 32); + delete[] pTempHash; + delete[] pLastHash; + //DONE BUILDING THE RESPONSE ***// + Server[-1] = Version; + RTMPStream::snd_cnt += 3073; + return true; +} diff --git a/util/rtmpchunks.h b/util/rtmpchunks.h new file mode 100644 index 00000000..b11142c8 --- /dev/null +++ b/util/rtmpchunks.h @@ -0,0 +1,65 @@ +/// \file rtmpchunks.h +/// Holds all headers for the RTMPStream namespace. + +#pragma once +#include +#include +#include +#include +#include +#include +#define DEBUG 4 + +/// Contains all functions and classes needed for RTMP connections. +namespace RTMPStream{ + + /// Gets the current system time in milliseconds. + unsigned int getNowMS(); + + extern unsigned int chunk_rec_max; ///< Maximum size for a received chunk. + extern unsigned int chunk_snd_max; ///< Maximum size for a sent chunk. + extern unsigned int rec_window_size; ///< Window size for receiving. + extern unsigned int snd_window_size; ///< Window size for sending. + extern unsigned int rec_window_at; ///< Current position of the receiving window. + extern unsigned int snd_window_at; ///< Current position of the sending window. + extern unsigned int rec_cnt; ///< Counter for total data received, in bytes. + extern unsigned int snd_cnt; ///< Counter for total data sent, in bytes. + + extern timeval lastrec; ///< Timestamp of last time data was received. + extern unsigned int firsttime; ///< Timestamp of first time a chunk was sent. + + /// Holds a single RTMP chunk, either send or receive direction. + class Chunk{ + public: + unsigned int cs_id; ///< ContentStream ID + unsigned int timestamp; ///< Timestamp of this chunk. + unsigned int len; ///< Length of the complete chunk. + unsigned int real_len; ///< Length of this particular part of it. + unsigned int len_left; ///< Length not yet received, out of complete chunk. + unsigned char msg_type_id; ///< Message Type ID + unsigned int msg_stream_id; ///< Message Stream ID + std::string data; ///< Payload of chunk. + + Chunk(); + bool Parse(std::string & data); + std::string Pack(); + + private: + static std::map lastsend; + static std::map lastrecv; + };//RTMPStream::Chunk + + std::string SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data); + std::string SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts); + std::string SendCTL(unsigned char type, unsigned int data); + std::string SendCTL(unsigned char type, unsigned int data, unsigned char data2); + std::string SendUSR(unsigned char type, unsigned int data); + std::string SendUSR(unsigned char type, unsigned int data, unsigned int data2); + + /// This value should be set to the first 1537 bytes received. + extern std::string handshake_in; + /// This value is the handshake response that is to be sent out. + extern std::string handshake_out; + /// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. + bool doHandshake(); +};//RTMPStream namespace diff --git a/util/server_setup.cpp b/util/server_setup.cpp index daa9e6c6..36e09c47 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -1,12 +1,34 @@ -#include +/// \file server_setup.cpp +/// Contains generic functions for setting up a DDVTECH Connector. + +#ifndef MAINHANDLER + /// Handler that is called for accepted incoming connections. + #define MAINHANDLER NoHandler + #error "No handler was set!" +#endif + + +#ifndef DEFAULT_PORT + /// Default port for this server. + #define DEFAULT_PORT 0 + #error "No default port was set!" +#endif + + +#ifndef CONFIGSECT + /// Configuration file section for this server. + #define CONFIGSECT None + #error "No configuration file section was set!" +#endif + #include "ddv_socket.h" //DDVTech Socket wrapper -#include "flv_tag.h" //FLV parsing with DDVTech Socket wrapper +#include #include #include #include -#define defstr(x) #x //converts a define name to string -#define defstrh(x) "[" defstr(x) "]" //converts define name to [string] -DDV::ServerSocket server_socket(-1); +#define defstr(x) #x ///< converts a define name to string +#define defstrh(x) "[" defstr(x) "]" ///< converts define name to [string] +DDV::ServerSocket server_socket(-1); ///< Placeholder for the server socket /// Basic signal handler. Disconnects the server_socket if it receives /// a SIGINT, SIGHUP or SIGTERM signal, but does nothing for SIGPIPE. @@ -26,10 +48,10 @@ void signal_handler (int signum){ /// Generic main entry point and loop for DDV Connectors. /// This sets up the proper termination handler, checks commandline options, /// parses config files and opens a listening socket on the requested port. -/// Any incoming connections will be accepted and start up the function MAINHANDLER, -/// which should be #defined before including server_setup.cpp. -/// The default port is set by #define DEFAULT_PORT. -/// The configuration file section is set by #define CONFIGSECT. +/// Any incoming connections will be accepted and start up the function #MAINHANDLER, +/// which should be defined before including server_setup.cpp. +/// The default port is set by define #DEFAULT_PORT. +/// The configuration file section is set by define #CONFIGSECT. int main(int argc, char ** argv){ DDV::Socket S;//placeholder for incoming connections From 4bdb7eb4e43d7171ea2c34dbed348f053e2ef2ec Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 16 Apr 2011 03:44:48 +0200 Subject: [PATCH 07/15] *evil scientist laughter* ITS ALIVE! ITS ALIVE! --- util/ddv_socket.cpp | 6 ++++-- util/flv_tag.cpp | 31 +++++++++++++++---------------- util/flv_tag.h | 2 ++ 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index e82ea8ae..3b3567ac 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -121,8 +121,9 @@ bool DDV::Socket::write(const void * buffer, int len){ return false; break; } + }else{ + sofar += r; } - sofar += r; } return true; }//DDv::Socket::write @@ -151,8 +152,9 @@ bool DDV::Socket::read(void * buffer, int len){ return false; break; } + }else{ + sofar += r; } - sofar += r; } return true; }//DDV::Socket::read diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 08fed05b..02494b56 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -143,7 +143,7 @@ void FLV::Tag::tagTime(unsigned int T){ /// The buffer length is initialized to 0, and later automatically /// increased if neccesary. FLV::Tag::Tag(){ - len = 0; buf = 0; data = 0; isKeyframe = false; + len = 0; buf = 0; data = 0; isKeyframe = false; done = true; sofar = 0; }//empty constructor /// Copy constructor, copies the contents of an existing tag. @@ -151,6 +151,8 @@ FLV::Tag::Tag(){ /// that is being copied, and later automaticallt increased if /// neccesary. FLV::Tag::Tag(const Tag& O){ + done = true; + sofar = 0; buf = O.len; len = buf; if (len > 0){ @@ -166,14 +168,18 @@ FLV::Tag::Tag(const Tag& O){ /// This operator checks for self-assignment. FLV::Tag & FLV::Tag::operator= (const FLV::Tag& O){ if (this != &O){//no self-assignment - if (data != 0){free(data);} - buf = O.len; - len = buf; + len = O.len; if (len > 0){ - data = (char*)malloc(len); + if (!data){ + data = (char*)malloc(len); + buf = len; + }else{ + if (buf < len){ + data = (char*)realloc(data, len); + buf = len; + } + } memcpy(data, O.data, len); - }else{ - data = 0; } isKeyframe = O.isKeyframe; } @@ -211,8 +217,6 @@ bool FLV::Tag::MemReadUntil(char * buffer, unsigned int count, unsigned int & so /// \param P The current position in the data buffer. Will be updated to reflect new position. /// \return True if a whole tag is succesfully read, false otherwise. bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ - static bool done = true; - static unsigned int sofar = 0; if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} if (done){ //read a header @@ -259,15 +263,14 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ /// \return True if count bytes are read succesfully, false otherwise. bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, DDV::Socket & sock){ if (sofar == count){return true;} - int r = sock.read(buffer + sofar,count-sofar); - if (r < 0){ + if (!sock.read(buffer + sofar,count-sofar)){ if (errno != EWOULDBLOCK){ FLV::Parse_Error = true; Error_Str = "Error reading from socket."; } return false; } - sofar += r; + sofar += count-sofar; if (sofar == count){return true;} if (sofar > count){ FLV::Parse_Error = true; @@ -282,8 +285,6 @@ bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & s /// \param sock The socket to read from. /// \return True if a whole tag is succesfully read, false otherwise. bool FLV::Tag::SockLoader(DDV::Socket sock){ - static bool done = true; - static unsigned int sofar = 0; if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} if (done){ if (SockReadUntil(data, 11, sofar, sock)){ @@ -354,8 +355,6 @@ bool FLV::Tag::FileLoader(FILE * f){ int preflags = fcntl(fileno(f), F_GETFL, 0); int postflags = preflags | O_NONBLOCK; fcntl(fileno(f), F_SETFL, postflags); - static bool done = true; - static unsigned int sofar = 0; if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} if (done){ diff --git a/util/flv_tag.h b/util/flv_tag.h index e824228c..0ba13ea5 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -35,6 +35,8 @@ namespace FLV { bool FileLoader(FILE * f); protected: int buf; ///< Maximum length of buffer space. + bool done; ///< Body reading done? + unsigned int sofar; ///< How many bytes are read sofar? //loader helper functions bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P); bool SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, DDV::Socket & sock); From 4f36bc0aa9360d330a27aa5ae72115a3afc57a82 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 17 Apr 2011 00:38:04 +0200 Subject: [PATCH 08/15] RTMP edits --- util/rtmpchunks.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index eca2f9fb..a63a2380 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -18,8 +18,8 @@ unsigned int RTMPStream::getNowMS(){ unsigned int RTMPStream::chunk_rec_max = 128; unsigned int RTMPStream::chunk_snd_max = 128; -unsigned int RTMPStream::rec_window_size = 0xFA00; -unsigned int RTMPStream::snd_window_size = 1024*500; +unsigned int RTMPStream::rec_window_size = 2500000; +unsigned int RTMPStream::snd_window_size = 2500000; unsigned int RTMPStream::rec_window_at = 0; unsigned int RTMPStream::snd_window_at = 0; unsigned int RTMPStream::rec_cnt = 0; @@ -243,6 +243,9 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned /// Parses the argument string into the current chunk. /// Tries to read a whole chunk, if successful it will remove /// the corresponding data from the input string. +/// If only part of a chunk is read, it will remove the part and call itself again. +/// This has the effect of only causing a "true" reponse in the case a *whole* chunk +/// is read, not just part of a chunk. /// \param indata The input string to parse and update. /// \warning This function will destroy the current data in this chunk! /// \returns True if a whole chunk could be read, false otherwise. @@ -368,7 +371,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ /// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. /// After calling this function, don't forget to read and ignore 1536 extra bytes, -/// this is the handshake response and not interesting for us because we don't do client +/// these are the handshake response and not interesting for us because we don't do client /// verification. bool RTMPStream::doHandshake(){ char Version; From 93528dfc1dfc3655e2c17f1624a5e40de20b50a0 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 17 Apr 2011 00:38:04 +0200 Subject: [PATCH 09/15] RTMP edits --- util/rtmpchunks.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index eca2f9fb..a63a2380 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -18,8 +18,8 @@ unsigned int RTMPStream::getNowMS(){ unsigned int RTMPStream::chunk_rec_max = 128; unsigned int RTMPStream::chunk_snd_max = 128; -unsigned int RTMPStream::rec_window_size = 0xFA00; -unsigned int RTMPStream::snd_window_size = 1024*500; +unsigned int RTMPStream::rec_window_size = 2500000; +unsigned int RTMPStream::snd_window_size = 2500000; unsigned int RTMPStream::rec_window_at = 0; unsigned int RTMPStream::snd_window_at = 0; unsigned int RTMPStream::rec_cnt = 0; @@ -243,6 +243,9 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned /// Parses the argument string into the current chunk. /// Tries to read a whole chunk, if successful it will remove /// the corresponding data from the input string. +/// If only part of a chunk is read, it will remove the part and call itself again. +/// This has the effect of only causing a "true" reponse in the case a *whole* chunk +/// is read, not just part of a chunk. /// \param indata The input string to parse and update. /// \warning This function will destroy the current data in this chunk! /// \returns True if a whole chunk could be read, false otherwise. @@ -368,7 +371,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ /// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. /// After calling this function, don't forget to read and ignore 1536 extra bytes, -/// this is the handshake response and not interesting for us because we don't do client +/// these are the handshake response and not interesting for us because we don't do client /// verification. bool RTMPStream::doHandshake(){ char Version; From 4608d55d34a96cd8514acb673066d09eaf7148ae Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 17 Apr 2011 04:04:00 +0200 Subject: [PATCH 10/15] AMF3 eerste poging --- util/amf.cpp | 561 ++++++++++++++++++++++++++++++++++++++++++++++++++- util/amf.h | 58 ++++++ 2 files changed, 618 insertions(+), 1 deletion(-) diff --git a/util/amf.cpp b/util/amf.cpp index 97d97ef5..938770a9 100644 --- a/util/amf.cpp +++ b/util/amf.cpp @@ -46,7 +46,7 @@ AMF::Object* AMF::Object::getContentP(std::string s){ for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ if (it->Indice() == s){return &(*it);} } - return this; + return 0; }; /// Returns a copy of the object held at indice s. @@ -59,6 +59,12 @@ AMF::Object AMF::Object::getContent(std::string s){ return AMF::Object("error", AMF0_DDV_CONTAINER); }; +/// Default constructor. +/// Simply fills the data with AMF::Object("error", AMF0_DDV_CONTAINER) +AMF::Object::Object(){ + *this = AMF::Object("error", AMF0_DDV_CONTAINER); +};//default constructor + /// Constructor for numeric objects. /// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. @@ -404,3 +410,556 @@ AMF::Object AMF::parse(const unsigned char * data, unsigned int len){ AMF::Object AMF::parse(std::string data){ return AMF::parse((const unsigned char*)data.c_str(), data.size()); }//parse + +/// Returns the std::string Indice for the current object, if available. +/// Returns an empty string if no indice exists. +std::string AMF::Object3::Indice(){return myIndice;}; + +/// Returns the AMF::obj0type AMF0 object type for this object. +AMF::obj3type AMF::Object3::GetType(){return myType;}; + +/// Returns the double value of this object, if available. +/// If this object holds no double value, 0 is returned. +double AMF::Object3::DblValue(){return dblval;}; + +/// Returns the integer value of this object, if available. +/// If this object holds no integer value, 0 is returned. +int AMF::Object3::IntValue(){return intval;}; + +/// Returns the std::string value of this object, if available. +/// If this object holds no string value, an empty string is returned. +std::string AMF::Object3::StrValue(){return strval;}; + +/// Returns the C-string value of this object, if available. +/// If this object holds no string value, an empty C-string is returned. +const char * AMF::Object3::Str(){return strval.c_str();}; + +/// Returns a count of the amount of objects this object currently holds. +/// If this object is not a container type, this function will always return 0. +int AMF::Object3::hasContent(){return contents.size();}; + +/// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types. +void AMF::Object3::addContent(AMF::Object3 c){contents.push_back(c);}; + +/// Returns a pointer to the object held at indice i. +/// Returns AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +AMF::Object3* AMF::Object3::getContentP(int i){return &contents.at(i);}; + +/// Returns a copy of the object held at indice i. +/// Returns a AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +AMF::Object3 AMF::Object3::getContent(int i){return contents.at(i);}; + +/// Returns a pointer to the object held at indice s. +/// Returns NULL if no object is held at this indice. +/// \param s The indice of the object in this container. +AMF::Object3* AMF::Object3::getContentP(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return &(*it);} + } + return 0; +}; + +/// Returns a copy of the object held at indice s. +/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param s The indice of the object in this container. +AMF::Object3 AMF::Object3::getContent(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return *it;} + } + return AMF::Object3("error", AMF3_DDV_CONTAINER); +}; + +/// Default constructor. +/// Simply fills the data with AMF::Object3("error", AMF3_DDV_CONTAINER) +AMF::Object3::Object3(){ + *this = AMF::Object3("error", AMF3_DDV_CONTAINER); +};//default constructor + +/// Constructor for double objects. +/// The object type is by default AMF::AMF3_DOUBLE, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The numeric value of this object. Double AMF3 objects only support double-type values. +/// \param setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, double val, AMF::obj3type setType){//num type initializer + myIndice = indice; + myType = setType; + strval = ""; + dblval = val; + intval = 0; +}; + +/// Constructor for integer objects. +/// The object type is by default AMF::AMF3_INTEGER, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The numeric value of this object. Integer AMF3 objects only support integer-type values. +/// \param setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, int val, AMF::obj3type setType){//num type initializer +myIndice = indice; +myType = setType; +strval = ""; +dblval = val; +intval = 0; +}; + +/// Constructor for string objects. +/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. +/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done automatically. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The string value of this object. +/// \param setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, std::string val, AMF::obj3type setType){//str type initializer + myIndice = indice; + myType = setType; + strval = val; + dblval = 0; + intval = 0; +}; + +/// Constructor for container objects. +/// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, AMF::obj3type setType){//object type initializer + myIndice = indice; + myType = setType; + strval = ""; + dblval = 0; + intval = 0; +}; + +/// Prints the contents of this object to std::cerr. +/// If this object contains other objects, it will call itself recursively +/// and print all nested content in a nice human-readable format. +void AMF::Object3::Print(std::string indent){ + std::cerr << indent; + // print my type + switch (myType){ + case AMF::AMF3_UNDEFINED: std::cerr << "Undefined"; break; + case AMF::AMF3_NULL: std::cerr << "Null"; break; + case AMF::AMF3_FALSE: std::cerr << "False"; break; + case AMF::AMF3_TRUE: std::cerr << "True"; break; + case AMF::AMF3_INTEGER: std::cerr << "Integer"; break; + case AMF::AMF3_DOUBLE: std::cerr << "Double"; break; + case AMF::AMF3_STRING: std::cerr << "String"; break; + case AMF::AMF3_XMLDOC: std::cerr << "XML Doc"; break; + case AMF::AMF3_DATE: std::cerr << "Date"; break; + case AMF::AMF3_ARRAY: std::cerr << "Array"; break; + case AMF::AMF3_OBJECT: std::cerr << "Object"; break; + case AMF::AMF3_XML: std::cerr << "XML"; break; + case AMF::AMF3_BYTES: std::cerr << "ByteArray"; break; + case AMF::AMF3_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; + } + // print my string indice, if available + std::cerr << " " << myIndice << " "; + // print my numeric or string contents + switch (myType){ + case AMF::AMF3_INTEGER: std::cerr << intval; break; + case AMF::AMF3_DOUBLE: std::cerr << dblval; break; + case AMF::AMF3_STRING: case AMF::AMF3_XMLDOC: case AMF::AMF3_XML: case AMF::AMF3_BYTES: + if (intval > 0){ + std::cerr << "REF" << intval; + }else{ + std::cerr << strval; + } + break; + case AMF::AMF3_DATE: + if (intval > 0){ + std::cerr << "REF" << intval; + }else{ + std::cerr << dblval; + } + break; + case AMF::AMF3_ARRAY: case AMF::AMF3_OBJECT: + if (intval > 0){ + std::cerr << "REF" << intval; + } + break; + default: break;//we don't care about the rest, and don't want a compiler warning... + } + std::cerr << std::endl; + // if I hold other objects, print those too, recursively. + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} + } +};//print + +/// Packs the AMF object to a std::string for transfer over the network. +/// If the object is a container type, this function will call itself recursively and contain all contents. +/// Tip: When sending multiple AMF objects in one go, put them in a single AMF::AMF0_DDV_CONTAINER for easy transfer. +std::string AMF::Object3::Pack(){ + std::string r = ""; + return r; +};//pack + +/// Parses a single AMF3 type - used recursively by the AMF::parse3() functions. +/// This function updates i every call with the new position in the data. +/// \param data The raw data to parse. +/// \param len The size of the raw data. +/// \param i Current parsing position in the raw data. +/// \param name Indice name for any new object created. +/// \returns A single AMF::Object3, parsed from the raw data. +AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ + std::string tmpstr; + unsigned int tmpi = 0; + unsigned int arrsize = 0; + unsigned char tmpdbl[8]; + #if DEBUG >= 10 + fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); + #endif + switch (data[i]){ + case AMF::AMF3_UNDEFINED: + case AMF::AMF3_NULL: + case AMF::AMF3_FALSE: + case AMF::AMF3_TRUE: + ++i; + return AMF::Object3(name, (AMF::obj3type)data[i-1]); + break; + case AMF::AMF3_INTEGER: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + return AMF::Object3(name, (int)tmpi, AMF::AMF3_INTEGER); + break; + case AMF::AMF3_DOUBLE: + tmpdbl[7] = data[i+1]; + tmpdbl[6] = data[i+2]; + tmpdbl[5] = data[i+3]; + tmpdbl[4] = data[i+4]; + tmpdbl[3] = data[i+5]; + tmpdbl[2] = data[i+6]; + tmpdbl[1] = data[i+7]; + tmpdbl[0] = data[i+8]; + i+=9;//skip 8(a double)+1 forwards + return AMF::Object3(name, *(double*)tmpdbl, AMF::AMF3_DOUBLE); + break; + case AMF::AMF3_STRING: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_STRING);//reference type + } + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data + i += (tmpi >> 1);//skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_STRING);//normal type + break; + case AMF::AMF3_XMLDOC: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XMLDOC);//reference type + } + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data + i += (tmpi >> 1);//skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_XMLDOC);//normal type + break; + case AMF::AMF3_XML: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XML);//reference type + } + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data + i += (tmpi >> 1);//skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_XML);//normal type + break; + case AMF::AMF3_BYTES: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_BYTES);//reference type + } + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data + i += (tmpi >> 1);//skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_BYTES);//normal type + break; + case AMF::AMF3_DATE: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_DATE);//reference type + } + tmpdbl[7] = data[i]; + tmpdbl[6] = data[i+1]; + tmpdbl[5] = data[i+2]; + tmpdbl[4] = data[i+3]; + tmpdbl[3] = data[i+4]; + tmpdbl[2] = data[i+5]; + tmpdbl[1] = data[i+6]; + tmpdbl[0] = data[i+7]; + i += 8;//skip a double forwards + return AMF::Object3(name, *(double*)tmpdbl, AMF::AMF3_DATE); + break; + case AMF::AMF3_ARRAY:{ + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_ARRAY);//reference type + } + AMF::Object3 ret(name, AMF::AMF3_ARRAY); + arrsize = tmpi >> 1; + do{ + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 4;//fix sign bit, ignore references for now... + /// \todo Fix references? + if (tmpi > 0){ + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i, (size_t)tmpi);//add the string data + ret.addContent(AMF::parseOne3(data, len, i, tmpstr));//add content, recursively parsed, updating i + } + }while(tmpi > 0); + while (arrsize > 0){//while not done parsing array + ret.addContent(AMF::parseOne3(data, len, i, "arrVal"));//add content, recursively parsed, updating i + --arrsize; + } + return ret; + } break; + case AMF::AMF3_OBJECT:{ + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_OBJECT);//reference type + } + AMF::Object3 ret(name, AMF::AMF3_OBJECT); + bool isdynamic = false; + if ((tmpi & 2) == 0){//traits by reference, skip for now + /// \todo Implement traits by reference. Or references in general, of course... + }else{ + isdynamic = ((tmpi & 8) == 8); + arrsize = tmpi >> 4;//count of sealed members + /// \todo Read in arrsize sealed member names, then arrsize sealed members. + } + if (isdynamic){ + do{ + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 4;//fix sign bit, ignore references for now... + /// \todo Fix references? + if (tmpi > 0){ + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i, (size_t)tmpi);//add the string data + ret.addContent(AMF::parseOne3(data, len, i, tmpstr));//add content, recursively parsed, updating i + } + }while(tmpi > 0);//keep reading dynamic values until empty string + }//dynamic types + return ret; + } break; + } + #if DEBUG >= 2 + fprintf(stderr, "Error: Unimplemented AMF3 type %hhx - returning.\n", data[i]); + #endif + return AMF::Object3("error", AMF::AMF3_DDV_CONTAINER); +}//parseOne + +/// Parses a C-string to a valid AMF::Object3. +/// This function will find all AMF3 objects in the string and return +/// them all packed in a single AMF::AMF3_DDV_CONTAINER AMF::Object3. +AMF::Object3 AMF::parse3(const unsigned char * data, unsigned int len){ + AMF::Object3 ret("returned", AMF::AMF3_DDV_CONTAINER);//container type + unsigned int i = 0, j = 0; + while (i < len){ + ret.addContent(AMF::parseOne3(data, len, i, "")); + if (i > j){j = i;}else{return ret;} + } + return ret; +}//parse + +/// Parses a std::string to a valid AMF::Object3. +/// This function will find all AMF3 objects in the string and return +/// them all packed in a single AMF::AMF3_DDV_CONTAINER AMF::Object3. +AMF::Object3 AMF::parse3(std::string data){ + return AMF::parse3((const unsigned char*)data.c_str(), data.size()); +}//parse diff --git a/util/amf.h b/util/amf.h index 8ae95cd4..61f95a8a 100644 --- a/util/amf.h +++ b/util/amf.h @@ -33,6 +33,24 @@ namespace AMF{ AMF0_DDV_CONTAINER = 0xFF }; + /// Enumerates all possible AMF3 types, adding a special DDVTECH container type for ease of use. + enum obj3type { + AMF3_UNDEFINED = 0x00, + AMF3_NULL = 0x01, + AMF3_FALSE = 0x02, + AMF3_TRUE = 0x03, + AMF3_INTEGER = 0x04, + AMF3_DOUBLE = 0x05, + AMF3_STRING = 0x06, + AMF3_XMLDOC = 0x07, + AMF3_DATE = 0x08, + AMF3_ARRAY = 0x09, + AMF3_OBJECT = 0x0A, + AMF3_XML = 0x0B, + AMF3_BYTES = 0x0C, + AMF3_DDV_CONTAINER = 0xFF + }; + /// Recursive class that holds AMF0 objects. /// It supports all AMF0 types (defined in AMF::obj0type), adding support for a special DDVTECH container type. class Object { @@ -48,6 +66,7 @@ namespace AMF{ Object getContent(int i); Object* getContentP(std::string s); Object getContent(std::string s); + Object(); Object(std::string indice, double val, obj0type setType = AMF0_NUMBER); Object(std::string indice, std::string val, obj0type setType = AMF0_STRING); Object(std::string indice, obj0type setType = AMF0_OBJECT); @@ -67,5 +86,44 @@ namespace AMF{ Object parse(std::string data); /// Parses a single AMF0 type - used recursively by the AMF::parse() functions. Object parseOne(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); + + /// Recursive class that holds AMF3 objects. + /// It supports all AMF3 types (defined in AMF::obj3type), adding support for a special DDVTECH container type. + class Object3 { + public: + std::string Indice(); + obj3type GetType(); + double DblValue(); + int IntValue(); + std::string StrValue(); + const char * Str(); + int hasContent(); + void addContent(AMF::Object3 c); + Object3* getContentP(int i); + Object3 getContent(int i); + Object3* getContentP(std::string s); + Object3 getContent(std::string s); + Object3(); + Object3(std::string indice, int val, obj3type setType = AMF3_INTEGER); + Object3(std::string indice, double val, obj3type setType = AMF3_DOUBLE); + Object3(std::string indice, std::string val, obj3type setType = AMF3_STRING); + Object3(std::string indice, obj3type setType = AMF3_OBJECT); + void Print(std::string indent = ""); + std::string Pack(); + protected: + std::string myIndice; ///< Holds this objects indice, if any. + obj3type myType; ///< Holds this objects AMF0 type. + std::string strval; ///< Holds this objects string value, if any. + double dblval; ///< Holds this objects double value, if any. + int intval; ///< Holds this objects int value, if any. + std::vector contents; ///< Holds this objects contents, if any (for container types). + };//AMFType + + /// Parses a C-string to a valid AMF::Object3. + Object3 parse3(const unsigned char * data, unsigned int len); + /// Parses a std::string to a valid AMF::Object3. + Object3 parse3(std::string data); + /// Parses a single AMF3 type - used recursively by the AMF::parse3() functions. + Object3 parseOne3(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); };//AMF namespace From 06e0aa40abe3ac96665f933edb4358dabaa72fc2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 17 Apr 2011 23:59:49 +0200 Subject: [PATCH 11/15] AMF3 support, RTMP werkendheid? --- util/amf.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/amf.cpp b/util/amf.cpp index 938770a9..b82aceeb 100644 --- a/util/amf.cpp +++ b/util/amf.cpp @@ -606,7 +606,7 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi unsigned int arrsize = 0; unsigned char tmpdbl[8]; #if DEBUG >= 10 - fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); + fprintf(stderr, "Note: AMF3 type %hhx found. %i bytes left\n", data[i], len-i); #endif switch (data[i]){ case AMF::AMF3_UNDEFINED: @@ -759,11 +759,11 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi }else{ tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. tmpi |= data[i+4]; + tmpi = (tmpi << 3) >> 3;//fix sign bit i+=5; } } } - tmpi = (tmpi << 3) >> 3;//fix sign bit if ((tmpi & 1) == 0){ return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_BYTES);//reference type } From 0f125ca0e93a74807f707f6a63bea202a24ed859 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Apr 2011 15:09:31 +0200 Subject: [PATCH 12/15] Socket bugfix --- util/ddv_socket.cpp | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 3b3567ac..c54cee42 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -106,21 +106,16 @@ bool DDV::Socket::connected(){ /// \returns True if the whole write was succesfull, false otherwise. bool DDV::Socket::write(const void * buffer, int len){ int sofar = 0; - Blocking = false; + if (sock < 0){return false;} while (sofar != len){ int r = send(sock, (char*)buffer + sofar, len-sofar, 0); if (r <= 0){ - switch (errno){ - case EWOULDBLOCK: Blocking = true; break; - default: - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not write data! Error: %s\n", strerror(errno)); - #endif - close(); - return false; - break; - } + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not write data! Error: %s\n", strerror(errno)); + #endif + close(); + return false; }else{ sofar += r; } @@ -137,21 +132,16 @@ bool DDV::Socket::write(const void * buffer, int len){ /// \returns True if the whole read was succesfull, false otherwise. bool DDV::Socket::read(void * buffer, int len){ int sofar = 0; - Blocking = false; + if (sock < 0){return false;} while (sofar != len){ int r = recv(sock, (char*)buffer + sofar, len-sofar, 0); if (r <= 0){ - switch (errno){ - case EWOULDBLOCK: Blocking = true; break; - default: - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not read data! Error: %s\n", strerror(errno)); - #endif - close(); - return false; - break; - } + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not read data! Error: %s\n", strerror(errno)); + #endif + close(); + return false; }else{ sofar += r; } From 08f78a8bedc06a3b81675bd6c34cc7d1d8651f8c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Apr 2011 15:19:26 +0200 Subject: [PATCH 13/15] Socket fixes deel 2 --- util/ddv_socket.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index c54cee42..08bfda65 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -176,6 +176,7 @@ int DDV::Socket::iwrite(void * buffer, int len){ break; } } + if (r == 0){close();} return r; }//DDV::Socket::iwrite @@ -199,6 +200,7 @@ int DDV::Socket::iread(void * buffer, int len){ break; } } + if (r == 0){close();} return r; }//DDV::Socket::iread From 8f3466ea7864831698810f3e2d2a629ccf6d12bb Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Apr 2011 15:42:14 +0200 Subject: [PATCH 14/15] Socket fixes 3 --- util/ddv_socket.cpp | 10 ++++++---- util/http_parser.cpp | 16 ++-------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 08bfda65..72ed286e 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -211,12 +211,14 @@ int DDV::Socket::iread(void * buffer, int len){ /// \return True if new data arrived, false otherwise. bool DDV::Socket::read(std::string & buffer){ char cbuffer[5000]; - int num = iread(cbuffer, 5000); + if (!read(cbuffer, 1)){return false;} + int num = iread(cbuffer+1, 4999); if (num > 0){ - buffer.append(cbuffer, num); - return true; + buffer.append(cbuffer, num+1); + }else{ + buffer.append(cbuffer, 1); } - return false; + return true; }//read /// Create a new base ServerSocket. The socket is never connected, and a placeholder for later connections. diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 60d370e7..8c9ed2db 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -134,20 +134,8 @@ bool HTTP::Parser::Read(DDV::Socket & sock){ int r = 0; int b = 0; char buffer[500]; - while (true){ - r = sock.ready(); - if (r < 1){ - if (r == -1){ - #if DEBUG >= 1 - fprintf(stderr, "User socket is disconnected.\n"); - #endif - } - return parse(); - } - b = sock.iread(buffer, 500); - HTTPbuffer.append(buffer, b); - } - return false; + sock.read(HTTPbuffer); + return parse(); }//HTTPReader::ReadSocket /// Reads a full set of HTTP responses/requests from file F. From 9ff67b96fd0aa2a7a72bc45e006d56b395a31a7a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Apr 2011 16:08:55 +0200 Subject: [PATCH 15/15] Hopelijk laatste socket fix --- util/http_parser.cpp | 9 ++++----- util/http_parser.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 8c9ed2db..4fe7b94f 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -128,12 +128,11 @@ void HTTP::Parser::SetVar(std::string i, std::string v){ } /// Attempt to read a whole HTTP request or response from DDV::Socket sock. +/// \param sock The socket to use. +/// \param nonblock When true, will not block even if the socket is blocking. /// \return True of a whole request or response was read, false otherwise. -bool HTTP::Parser::Read(DDV::Socket & sock){ - //returned true als hele http packet gelezen is - int r = 0; - int b = 0; - char buffer[500]; +bool HTTP::Parser::Read(DDV::Socket & sock, bool nonblock){ + if (nonblock && (sock.ready() < 1)){return parse();} sock.read(HTTPbuffer); return parse(); }//HTTPReader::ReadSocket diff --git a/util/http_parser.h b/util/http_parser.h index d08cadef..ed8f64fe 100644 --- a/util/http_parser.h +++ b/util/http_parser.h @@ -14,7 +14,7 @@ namespace HTTP{ class Parser{ public: Parser(); - bool Read(DDV::Socket & sock); + bool Read(DDV::Socket & sock, bool nonblock = true); bool Read(FILE * F); std::string GetHeader(std::string i); std::string GetVar(std::string i);