merge fix

This commit is contained in:
Erik Zandvliet 2011-06-08 13:56:05 +02:00
commit 059ec80bf1
27 changed files with 3453 additions and 2468 deletions

296
Doxyfile Normal file
View file

@ -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 = YES
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

View file

@ -1,22 +0,0 @@
// C++ Socket Wrapper
//
// Started 020316
//
// License: LGPL v2.1+ (see the file LICENSE)
// (c)2002-2003 Anders Lindstr<74>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

View file

@ -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<74>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 <errno.h>
#include <new>
#include <time.h>
#include <stdio.h>
#include <string.h>
#ifndef __WIN32__
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#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;
}
}
}

View file

@ -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 <unistd.h>
#include <string>
// 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 */

View file

@ -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 <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#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;
}

View file

@ -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 <string>
// 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 */

View file

@ -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<74>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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#else
#include <winsock2.h>
#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 */

View file

@ -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 <fcntl.h>
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__ */

View file

@ -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 <string>
// 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 */

View file

@ -1,5 +1,6 @@
#pragma once
#include <arpa/inet.h>
#include <stdint.h>
#include <cstdlib>
#include <cstdio>

File diff suppressed because it is too large Load diff

129
util/amf.h Normal file
View file

@ -0,0 +1,129 @@
/// \file amf.h
/// Holds all headers for the AMF namespace.
#pragma once
#include <vector>
#include <iostream>
//#include <string.h>
#include <string>
/// 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
};
/// 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 {
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();
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<Object> 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);
/// 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<Object3> 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

509
util/crypto.cpp Normal file
View file

@ -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;
}

56
util/crypto.h Normal file
View file

@ -0,0 +1,56 @@
/// \file crypto.h
/// Holds all headers needed for RTMP cryptography functions.
#pragma once
#include <stdint.h>
#include <string>
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/rc4.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/hmac.h>
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);

View file

@ -1,183 +1,355 @@
#pragma once
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
/// \file ddv_socket.cpp
/// Holds all code for the DDV namespace.
bool socketError = false;
bool socketBlocking = false;
#include "ddv_socket.h"
int DDV_OpenUnix(std::string adres, bool nonblock = false){
int s = socket(PF_UNIX, SOCK_STREAM, 0);
/// 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
/// 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
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){
int sofar = 0;
socketBlocking = false;
while (sofar != todo){
int r = recv(sock, (char*)buffer + sofar, todo-sofar, 0);
if (r <= 0){
switch (errno){
case EWOULDBLOCK: socketBlocking = true; break;
default:
socketError = true;
fprintf(stderr, "Could not read! %s\n", strerror(errno));
return false;
break;
}
}
sofar += r;
}
return true;
/// 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);
}
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);}
/// 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;
if (sock < 0){return false;}
while (sofar != len){
int r = send(sock, (char*)buffer + sofar, len-sofar, 0);
if (r <= 0){
Error = true;
#if DEBUG >= 2
fprintf(stderr, "Could not write data! Error: %s\n", strerror(errno));
#endif
close();
return false;
}else{
sofar += r;
}
}
return true;
}//DDv::Socket::write
int DDV_iwrite(void * buffer, int todo, int sock){
int r = send(sock, buffer, todo, 0);
/// 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;
if (sock < 0){return false;}
while (sofar != len){
int r = recv(sock, (char*)buffer + sofar, len-sofar, 0);
if (r <= 0){
Error = true;
#if DEBUG >= 2
fprintf(stderr, "Could not read data! Error: %s\n", strerror(errno));
#endif
close();
return false;
}else{
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());}
/// 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;
}
}
if (r == 0){close();}
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;
}
}
if (r == 0){close();}
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];
if (!read(cbuffer, 1)){return false;}
int num = iread(cbuffer+1, 4999);
if (num > 0){
buffer.append(cbuffer, num+1);
}else{
buffer.append(cbuffer, 1);
}
return true;
}//read
/// 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);
//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;
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;}

58
util/ddv_socket.h Normal file
View file

@ -0,0 +1,58 @@
/// \file ddv_socket.h
/// Holds all headers for the DDV namespace.
#pragma once
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
///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(); ///< 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.
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.
bool read(std::string & buffer); ///< Read call that is compatible with std::string.
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.
};
};

View file

@ -1,88 +0,0 @@
#include <unistd.h> //for read()
#include <fcntl.h>
#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

View file

@ -1,87 +0,0 @@
#include <unistd.h> //for read()
#include <fcntl.h>
#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

View file

@ -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

View file

@ -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

394
util/flv_tag.cpp Normal file
View file

@ -0,0 +1,394 @@
/// \file flv_tag.cpp
/// Holds all code for the FLV namespace.
#include "flv_tag.h"
#include <stdio.h> //for Tag::FileLoader
#include <unistd.h> //for Tag::FileLoader
#include <fcntl.h> //for Tag::FileLoader
#include <stdlib.h> //malloc
#include <string.h> //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.
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:
/// - 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; done = true; sofar = 0;
}//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){
done = true;
sofar = 0;
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
len = O.len;
if (len > 0){
if (!data){
data = (char*)malloc(len);
buf = len;
}else{
if (buf < len){
data = (char*)realloc(data, len);
buf = len;
}
}
memcpy(data, O.data, len);
}
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){
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; Error_Str = "Invalid header received."; 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; Error_Str = "Invalid Tag received."; 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;}
if (!sock.read(buffer + sofar,count-sofar)){
if (errno != EWOULDBLOCK){
FLV::Parse_Error = true;
Error_Str = "Error reading from socket.";
}
return false;
}
sofar += count-sofar;
if (sofar == count){return true;}
if (sofar > count){
FLV::Parse_Error = true;
Error_Str = "Socket buffer overflow.";
}
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){
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; Error_Str = "Invalid header received."; 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; Error_Str = "Invalid Tag received."; 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; Error_Str = "File reading error."; 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);
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; Error_Str = "Invalid header received."; 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; Error_Str = "Invalid Tag received."; return false;}
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

46
util/flv_tag.h Normal file
View file

@ -0,0 +1,46 @@
/// \file flv_tag.h
/// Holds all headers for the FLV namespace.
#pragma once
#include "ddv_socket.h"
#include <string>
/// 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.
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".
/// 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.
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);
bool FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f);
};//Tag
};//FLV namespace

View file

@ -1,45 +1,14 @@
#pragma once
#include "ddv_socket.cpp"
#include <map>
#include <stdlib.h>
#include <stdio.h>
/// \file http_parser.cpp
/// Holds all code for the HTTP namespace.
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<std::string, std::string> headers;
std::map<std::string, std::string> vars;
void Trim(std::string & s);
};//HTTPReader
#include "http_parser.h"
HTTPReader::HTTPReader(){Clean();}
void HTTPReader::Clean(){
/// 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";
@ -48,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";
@ -60,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<std::string, std::string>::iterator it;
std::string tmp = method+" "+url+" "+protocol+"\n";
for (it=headers.begin(); it != headers.end(); it++){
@ -76,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<std::string, std::string>::iterator it;
std::string tmp = protocol+" "+code+" "+message+"\n";
for (it=headers.begin(); it != headers.end(); it++){
@ -87,69 +75,71 @@ 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::ReadSocket(int CONN_fd){
//returned true als hele http packet gelezen is
int r = 0;
int b = 0;
char buffer[500];
while (true){
r = DDV_ready(CONN_fd);
if (r < 1){
if (r == 0){
socketError = true;
#if DEBUG >= 1
fprintf(stderr, "User socket is disconnected.\n");
#endif
}
/// 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, bool nonblock){
if (nonblock && (sock.ready() < 1)){return parse();}
sock.read(HTTPbuffer);
return parse();
}
b = DDV_iread(buffer, 500, CONN_fd);
HTTPbuffer.append(buffer, b);
}
return false;
}//HTTPReader::ReadSocket
bool HTTPReader::ReadSocket(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];
@ -160,7 +150,11 @@ bool HTTPReader::ReadSocket(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 != ""){
@ -178,7 +172,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;
@ -194,7 +188,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);
@ -210,23 +204,40 @@ bool HTTPReader::parse(){
return false; //we should never get here...
}//HTTPReader::parse
void HTTPReader::SendResponse(int 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);
DDV_write(tmp.c_str(), tmp.size(), conn);
conn.write(tmp);
}
void HTTPReader::SendBodyPart(int 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(int conn, std::string bodypart){
/// 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());
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);
}else{
conn.write(bodypart);
}
}

47
util/http_parser.h Normal file
View file

@ -0,0 +1,47 @@
/// \file http_parser.h
/// Holds all headers for the HTTP namespace.
#pragma once
#include <map>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include "ddv_socket.h"
/// 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 nonblock = true);
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<std::string, std::string> headers;
std::map<std::string, std::string> vars;
void Trim(std::string & s);
};//HTTP::Parser class
};//HTTP namespace

447
util/rtmpchunks.cpp Normal file
View file

@ -0,0 +1,447 @@
/// \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 = 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;
unsigned int RTMPStream::snd_cnt = 0;
timeval RTMPStream::lastrec;
unsigned int RTMPStream::firsttime;
/// Holds the last sent chunk for every msg_id.
std::map<unsigned int, RTMPStream::Chunk> RTMPStream::Chunk::lastsend;
/// Holds the last received chunk for every msg_id.
std::map<unsigned int, RTMPStream::Chunk> 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.
/// 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.
bool RTMPStream::Chunk::Parse(std::string & indata){
gettimeofday(&RTMPStream::lastrec, 0);
unsigned int i = 0;
if (indata.size() < 1) return false;//need at least a byte
unsigned char chunktype = indata[i++];
//read the chunkstream ID properly
switch (chunktype & 0x3F){
case 0:
if (indata.size() < 2) return false;//need at least 2 bytes to continue
cs_id = indata[i++] + 64;
break;
case 1:
if (indata.size() < 3) return false;//need at least 3 bytes to continue
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 Parse(indata);
}
}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,
/// these are 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;
}

65
util/rtmpchunks.h Normal file
View file

@ -0,0 +1,65 @@
/// \file rtmpchunks.h
/// Holds all headers for the RTMPStream namespace.
#pragma once
#include <map>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string>
#include <arpa/inet.h>
#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<unsigned int, Chunk> lastsend;
static std::map<unsigned int, Chunk> 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

View file

@ -1,27 +1,63 @@
int mainHandler(int CONN_fd);//define this function in your own code!
#include <signal.h>
#include "ddv_socket.cpp" //DDVTech Socket wrapper
#include "flv_sock.cpp" //FLV parsing with DDVTech Socket wrapper
int server_socket = 0;
/// \file server_setup.cpp
/// Contains generic functions for setting up a DDVTECH Connector.
void termination_handler (int signum){
if (server_socket == 0) return;
#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 <signal.h>
#include <sys/types.h>
#include <pwd.h>
#include <fstream>
#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.
/// 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;
case SIGHUP: break;
case SIGTERM: break;
default: return; break;
}
close(server_socket);
server_socket = 0;
}
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){
int CONN_fd = 0;
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);
@ -29,37 +65,82 @@ int main(int argc, char ** argv){
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:h?";
static const char *optString = "ndp:i:u:c:h?";
static const struct option longOpts[] = {
{"help",0,0,'h'},
{"port",1,0,'p'},
{"no-daemon",0,0,'n'}
{"interface",1,0,'i'},
{"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 '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
server_socket = DDV_Listen(listen_port);
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, "Made a listening socket on port %i...\n", listen_port);
fprintf(stderr, "Configuration file %s not found - using build-in defaults...\n", configfile.c_str());
#endif
if (server_socket > 0){
}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
@ -72,23 +153,41 @@ int main(int argc, char ** argv){
#endif
return 1;
}
int status;
while (server_socket > 0){
waitpid((pid_t)-1, &status, WNOHANG);
CONN_fd = DDV_Accept(server_socket);
if (CONN_fd > 0){
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 handling socket %i\n", (int)myid, CONN_fd);
fprintf(stderr, "Changed user to %s\n", username.c_str());
#endif
}
}
}
if (server_socket <= 0){
return 0;
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
}
return mainHandler(CONN_fd);
}
}
}//while connected
return 0;
}//main